﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.CPlusPlus;
using OpenCvSharp.Extensions;
using System.Threading;
using System.Diagnostics;

namespace OpenCvSharp_App
{
    public partial class Form1 : Form
    {
        #region Variables

        //changable vars--------------------------------------------------------------------------

        double autoteachdiesizeratio = 0.95;
        int Zscoreunit = 6;
        double Zscorethres = 2;

        //----------------------------------------------------------------------------------------

        Mat img = new Mat();
        bool queuemode = false;
        enum Algos
        {
            Grayscale,
            Threshold,
            Gaussian,
            Hough1,
            Hough2,
            HoughCircle,
            Canny,
            Fourier,
            Invert,
            FindBlob,
            Equalize,
            AdaptiveThres,
            Erosion,
            Dilation,
            Contour,
            FloodFill,
            Gabor,
            Watershed,
            Distance,
            AveGabor,
            Kmeans,
            Thinning,
            FitGrid,
            TwoGabor,
            Sharpen,
            DieHough,
            Median
        }
        List<Algos> lstAlgo = new List<Algos>();
        const int HOUGHLINECOUNT = 50;
        List<OpenCvSharp.CPlusPlus.Point> blobcentroids = new List<OpenCvSharp.CPlusPlus.Point>();
        Mat kmeansglobal = new Mat();
        bool autostart = false;
        bool fullauto = false;
        bool diematchmode = false; //false = dienum, true = minscore
        Mat dietemplate = new Mat();
        const int Xdiff = 20;
        const int Ydiff = 20;
        int totalmatch = 0;
        //OpenCvSharp.CPlusPlus.Point[] matchpos;
        List<OpenCvSharp.CPlusPlus.Point> matchpos = new List<OpenCvSharp.CPlusPlus.Point>();
        int rownum = 0;
        int colnum = 0;
        List<double> rowval = new List<double>();
        List<double> colval = new List<double>();
        int[,] diemap;
        int fouriergridwidth = 0;
        int fouriergridheight = 0;
        int fourierdiematchnum = 0;
        Stopwatch timer = new Stopwatch();

        Mat diecorner1 = new Mat();
        Mat diecorner2 = new Mat();
        Mat diecornermean = new Mat();
        Mat diecornerSD = new Mat();
        int diecornerstage = 0;

        #endregion

        #region Initialization

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            grpQueue.Enabled = false;

            cboAdaptiveType.Items.Add("Mean");
            cboAdaptiveType.Items.Add("Gaussian");
            cboAdaptiveType.SelectedIndex = 0;

            cboErosionShape.Items.Add("Rect");
            cboErosionShape.Items.Add("Cross");
            cboErosionShape.Items.Add("Ellipse");
            cboErosionShape.SelectedIndex = 0;

            cboDilationShape.Items.Add("Rect");
            cboDilationShape.Items.Add("Cross");
            cboDilationShape.Items.Add("Ellipse");
            cboDilationShape.SelectedIndex = 0;

            cboKmeansFlag.Items.Add("PPCen");
            cboKmeansFlag.Items.Add("RandCen");
            cboKmeansFlag.Items.Add("UseIniLbl");
            cboKmeansFlag.Items.Add("Zero");
            cboKmeansFlag.SelectedIndex = 0;

            cboKmeansTermcrit.Items.Add("Eps");
            cboKmeansTermcrit.Items.Add("Ite");
            cboKmeansTermcrit.Items.Add("Num");
            cboKmeansTermcrit.SelectedIndex = 0;

            cboFitGridMethod.Items.Add("SQDIFF");
            cboFitGridMethod.Items.Add("SQDIFF_N");
            cboFitGridMethod.Items.Add("CCORR");
            cboFitGridMethod.Items.Add("CCORR_N");
            cboFitGridMethod.Items.Add("CCOEFF");
            cboFitGridMethod.Items.Add("CCOEFF_N");
            cboFitGridMethod.SelectedIndex = 1;

            cboKmeansFilter.Items.Add("All");
            cboKmeansFilter.SelectedIndex = 0;
            cboKmeansFilter.Enabled = false;
        }

        #endregion

        #region Methods

        #region General Methods

        private void displayImg(Mat disp)
        {
            Bitmap pic = BitmapConverter.ToBitmap(disp);
            imgDisplay.Image = pic;
        }

        private void displayDie(Mat disp)
        {
            Bitmap pic = BitmapConverter.ToBitmap(disp);
            DieDisplay.Image = pic;
        }

        private void displayMean(Mat disp)
        {
            Bitmap pic = BitmapConverter.ToBitmap(disp);
            MeanDisplay.Image = pic;
        }

        private void displaySD(Mat disp)
        {
            Bitmap pic = BitmapConverter.ToBitmap(disp);
            SDDisplay.Image = pic;
        }

        private void WriteToLog(String input)
        {
            statuslog.AppendText(input + "\n");
            statuslog.ScrollToCaret();
        }

        private void UpdateTimer(string timestring)
        {
            lblTimeTaken.Text = timestring;
        }

        #endregion

        #region Filters/Algos

        private Mat toGrayscale(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat();
            Cv2.CvtColor(src, output, ColorConversion.BgrToGray);

            if(!autostart)
                displayImg(output);

            return output;
        }

        private Mat toBW(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat();
            int th = 0;
            th = Convert.ToInt32(txtboxThreshold.Text.ToString());
            Cv2.Threshold(src, output, (double)th, 255, ThresholdType.Binary);
            displayImg(output);

            return output;
        }

        private Mat toGaussian(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat();
            int sz = Convert.ToInt32(txtboxGaussianSize.Text.ToString());
            int sigma = Convert.ToInt32(txtboxGaussianSigma.Text.ToString());
            output = src.Clone();

            if (autostart)
            {
                sz = 35;
                sigma = 0;
            }

            OpenCvSharp.CPlusPlus.Size size = new OpenCvSharp.CPlusPlus.Size(sz, sz);

            Cv2.GaussianBlur(src, output, size, sigma);
            displayImg(output);

            return output;
        }

        private Mat toCanny(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat();
            Cv2.Canny(src, output, 50, 200, 3, false);
            displayImg(output);

            return output;
        }

        private Mat toHough(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            // Run Standard Hough Transform 
            Mat output = new Mat();

            output = img.Clone();
            CvLineSegmentPolar[] segStd = Cv2.HoughLines(src, 1, Math.PI / 180, 50, 0, 0);
            WriteToLog("Hough1: " + segStd.Length.ToString() + " lines found");

            /*
            List<CvLineSegmentPolar> houghlines = new List<CvLineSegmentPolar>();
            foreach (CvLineSegmentPolar line in segStd)
            {
                houghlines.Add(line);
            }

            List<List<CvLineSegmentPolar>> houghlinesgrp = new List<List<CvLineSegmentPolar>>();
            int ptcount = 0;
            while (ptcount < houghlines.Count)
            {
                double firstangle = 0;
                List<CvLineSegmentPolar> hls = new List<CvLineSegmentPolar>();
                for (int i = 0; i < houghlines.Count; i++)
                {
                    double angle = (houghlines[i].Theta / Math.PI) * 180;
                    if (angle >= 0)
                    {
                        firstangle = angle;
                        break;
                    }
                }

                for (int i = 0; i < houghlines.Count; i++)
                {
                    double angle = (houghlines[i].Theta / Math.PI) * 180;
                    if ((angle >= (firstangle - 5)) && (angle <= (firstangle + 5)))
                    {
                        hls.Add(houghlines[i]);
                        houghlines[i] = new CvLineSegmentPolar(-1000, -1000);
                        ptcount++;
                    }
                }
                houghlinesgrp.Add(hls);
            }

            List<CvLineSegmentPolar> topgrp1 = houghlinesgrp[0];
            List<CvLineSegmentPolar> topgrp2 = houghlinesgrp[1];

            foreach (List<CvLineSegmentPolar> grp in houghlinesgrp)
            {
                if (grp.Count > topgrp1.Count)
                    topgrp1 = grp;
                else if (grp.Count > topgrp2.Count)
                    topgrp2 = grp;
            }
            WriteToLog("Hough1, grpnum: " + houghlinesgrp.Count.ToString() + " grps");
            WriteToLog("Hough1, topgrp1: " + topgrp1.Count.ToString() + " lines");
            WriteToLog("Hough1, topgrp2: " + topgrp2.Count.ToString() + " lines");
            double topave1 = 0;
            double topave2 = 0;
            foreach (CvLineSegmentPolar line in topgrp1)
            {
                float rho = line.Rho;
                float theta = line.Theta;
                double a = Math.Cos(theta);
                double b = Math.Sin(theta);
                double x0 = a * rho;
                double y0 = b * rho;
                OpenCvSharp.CPlusPlus.Point pt1 = new OpenCvSharp.CPlusPlus.Point { X = Cv.Round(x0 + img.Width * (-b)), Y = Cv.Round(y0 + img.Width * (a)) };
                OpenCvSharp.CPlusPlus.Point pt2 = new OpenCvSharp.CPlusPlus.Point { X = Cv.Round(x0 - img.Width * (-b)), Y = Cv.Round(y0 - img.Width * (a)) };
                output.Line(pt1, pt2, Scalar.Red, 5);

                topave1 += line.Theta;
            }
            topave1 /= topgrp1.Count;
            foreach (CvLineSegmentPolar line in topgrp2)
            {
                float rho = line.Rho;
                float theta = line.Theta;
                double a = Math.Cos(theta);
                double b = Math.Sin(theta);
                double x0 = a * rho;
                double y0 = b * rho;
                OpenCvSharp.CPlusPlus.Point pt1 = new OpenCvSharp.CPlusPlus.Point { X = Cv.Round(x0 + img.Width * (-b)), Y = Cv.Round(y0 + img.Width * (a)) };
                OpenCvSharp.CPlusPlus.Point pt2 = new OpenCvSharp.CPlusPlus.Point { X = Cv.Round(x0 - img.Width * (-b)), Y = Cv.Round(y0 - img.Width * (a)) };
                output.Line(pt1, pt2, Scalar.Red, 5);

                topave2 += line.Theta;
            }
            topave2 /= topgrp2.Count;
            WriteToLog("Hough1, topave1: " + topave1.ToString());
            WriteToLog("Hough1, topave2: " + topave2.ToString());
            */
            /*
            for (int i = houghlines.Count - 1; i >= 0; i--)
            {
                double[] lineparam = new double[2];
                lineparam = houghlines[i];
                double angle = (lineparam[1] / Math.PI) * 180;
                if (!((angle > 355) && (angle < 5)))
                {
                    if (!((angle > 175) && (angle < 185)))
                    {
                        houghlines.RemoveAt(i);
                    }
                }
            }
            WriteToLog("Hough1, removed: " + houghlines.Count.ToString() + " lines left");
            */
            
            int limit = Math.Min(segStd.Length, HOUGHLINECOUNT);
            for (int i = 0; i < limit; i++)
            {
                // Draws result lines
                float rho = segStd[i].Rho;
                float theta = segStd[i].Theta;
                double a = Math.Cos(theta);
                double b = Math.Sin(theta);
                double x0 = a * rho;
                double y0 = b * rho;
                OpenCvSharp.CPlusPlus.Point pt1 = new OpenCvSharp.CPlusPlus.Point { X = Cv.Round(x0 + img.Width * (-b)), Y = Cv.Round(y0 + img.Width * (a)) };
                OpenCvSharp.CPlusPlus.Point pt2 = new OpenCvSharp.CPlusPlus.Point { X = Cv.Round(x0 - img.Width * (-b)), Y = Cv.Round(y0 - img.Width * (a)) };
                output.Line(pt1, pt2, Scalar.Red, 5);
            }
            

            displayImg(output);

            return output;
        }

        private Mat toHough2(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            // Run Probabilistic Hough Transform
            Mat output = new Mat();

            output = img.Clone();
            CvLineSegmentPoint[] segProb = Cv2.HoughLinesP(src, 1, Math.PI / 180, 50, 50, 10);
            WriteToLog("Hough2: " + segProb.Length.ToString() + " lines found");
            List<CvLineSegmentPoint> hl2 = new List<CvLineSegmentPoint>();
            foreach (CvLineSegmentPoint s in segProb)
            {
                hl2.Add(s);
                //output.Line(s.P1, s.P2, CvColor.Red, 3, LineType.AntiAlias, 0);
            }
            for (int i = hl2.Count - 1; i >= 0; i--)
            {
                double a = Math.Pow(hl2[i].P1.X - hl2[i].P2.X, 2);
                double b = Math.Pow(hl2[i].P1.Y - hl2[i].P2.Y, 2);
                double length = Math.Sqrt(a + b);
                if (length < (img.Height * 0.6))
                    hl2.RemoveAt(i);
            }
            WriteToLog("Hough2, after elim: " + hl2.Count.ToString() + " lines left");
            Random rnd = new Random();
            foreach (CvLineSegmentPoint s in hl2)
            {
                int rnd1 = rnd.Next(0, 255);
                int rnd2 = rnd.Next(0, 255);
                int rnd3 = rnd.Next(0, 255);
                output.Line(s.P1, s.P2, new CvColor(rnd1, rnd2, rnd3), 3, LineType.AntiAlias, 0);
            }
            return output;
        }

        private Mat toDieHough(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            // Run Probabilistic Hough Transform on wafer die
            Mat output = new Mat();
            int houghwidth = Convert.ToInt32(txtboxFitGridWidth.Text);
            int houghheight = Convert.ToInt32(txtboxFitGridHeight.Text);
            double ratio = Convert.ToDouble(txtboxFitGridRatio.Text);
            int HoughWidth = (int)(houghwidth / ratio);
            int HoughHeight = (int)(houghheight / ratio);
            int minlength = 0;

            if (houghwidth <= houghheight)
                minlength = HoughWidth;
            else
                minlength = HoughHeight;

            output = img.Clone();
            CvLineSegmentPoint[] segProb = Cv2.HoughLinesP(src, 1, Math.PI / 180, 50, minlength-10, (int)(minlength/10));
            WriteToLog("DieHough: " + segProb.Length.ToString() + " lines found");

            foreach (CvLineSegmentPoint s in segProb)
            {
                output.Line(s.P1, s.P2, CvColor.Red, 3, LineType.AntiAlias, 0);
            }
            displayImg(output);

            return output;
        }

        private Mat toHoughCircle(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat();

            output = img.Clone();
            CvCircleSegment[] segCirc = Cv2.HoughCircles(src, HoughCirclesMethod.Gradient, 80, 10, 200, 100, 0, 0);
            WriteToLog("HoughCirc: " + segCirc.Length.ToString() + " circles found");
            foreach (CvCircleSegment s in segCirc)
            {
                output.Circle((int)s.Center.X, (int)s.Center.Y, (int)s.Radius, CvColor.Red, 3, LineType.AntiAlias, 0);
            }
            displayImg(output);

            return output;
        }

        private Mat toFourier(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            // expand input image to optimal size
            Mat padded = new Mat();
            int m = Cv2.GetOptimalDFTSize(src.Rows);
            int n = Cv2.GetOptimalDFTSize(src.Cols); // on the border add zero values
            Cv2.CopyMakeBorder(src, padded, 0, m - src.Rows, 0, n - src.Cols, BorderType.Constant, Scalar.All(0));

            // Add to the expanded another plane with zeros
            Mat paddedF32 = new Mat();
            padded.ConvertTo(paddedF32, MatType.CV_32F);
            Mat[] planes = { paddedF32, Mat.Zeros(padded.Size(), MatType.CV_32F) };
            Mat complex = new Mat();
            Cv2.Merge(planes, complex);

            // this way the result may fit in the source matrix
            Mat dft = new Mat();
            Cv2.Dft(complex, dft);

            // compute the magnitude and switch to logarithmic scale
            // => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
            Mat[] dftPlanes;
            Cv2.Split(dft, out dftPlanes);  // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))

            // planes[0] = magnitude
            Mat magnitude = new Mat();
            Cv2.Magnitude(dftPlanes[0], dftPlanes[1], magnitude);

            magnitude += Scalar.All(1);  // switch to logarithmic scale
            Cv2.Log(magnitude, magnitude);

            // crop the spectrum, if it has an odd number of rows or columns
            Mat spectrum = magnitude[
                new Rect(0, 0, magnitude.Cols & -2, magnitude.Rows & -2)];

            // rearrange the quadrants of Fourier image  so that the origin is at the image center
            int cx = spectrum.Cols / 2;
            int cy = spectrum.Rows / 2;

            Mat q0 = new Mat(spectrum, new Rect(0, 0, cx, cy));   // Top-Left - Create a ROI per quadrant
            Mat q1 = new Mat(spectrum, new Rect(cx, 0, cx, cy));  // Top-Right
            Mat q2 = new Mat(spectrum, new Rect(0, cy, cx, cy));  // Bottom-Left
            Mat q3 = new Mat(spectrum, new Rect(cx, cy, cx, cy)); // Bottom-Right

            // swap quadrants (Top-Left with Bottom-Right)
            Mat tmp = new Mat();
            q0.CopyTo(tmp);
            q3.CopyTo(q0);
            tmp.CopyTo(q3);

            // swap quadrant (Top-Right with Bottom-Left)
            q1.CopyTo(tmp);
            q2.CopyTo(q1);
            tmp.CopyTo(q2);

            // Transform the matrix with float values into a
            Cv2.Normalize(spectrum, spectrum, 0, 1, NormType.MinMax);
            Mat output = new Mat();
            double min = 0;
            double max = 0;
            Cv2.MinMaxLoc(spectrum, out min, out max);
            WriteToLog("Min: " + min.ToString() + ", Max: " + max.ToString());
            spectrum = spectrum * 255;
            spectrum.ConvertTo(output, MatType.CV_8U);

            // Show the result
            //Cv2.ImShow("Input Image", src);
            //Cv2.ImShow("Spectrum Magnitude", spectrum);
            //Cv2.ImShow("DFT", dft);
            //spectrum.ConvertTo(spectrum, MatType.CV_8U);
            displayImg(output);

            return output;
        }

        private Mat toInvert(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat();

            Cv2.BitwiseNot(src, output);
            displayImg(output);

            return output;
        }

        private Mat toFindBlob(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat();

            float Minarea = (float)Convert.ToInt32(txtboxBlobMin.Text.ToString());
            float Maxarea = (float)Convert.ToInt32(txtboxBlobMax.Text.ToString());

            SimpleBlobDetector.Params pm = new SimpleBlobDetector.Params();
            pm.MinDistBetweenBlobs = 50.0f;
            pm.FilterByInertia = false;
            pm.FilterByConvexity = false;
            pm.FilterByColor = false;
            pm.FilterByCircularity = false;
            pm.FilterByArea = true;
            pm.MinArea = Minarea;
            pm.MaxArea = Maxarea;

            SimpleBlobDetector blobdetector = new SimpleBlobDetector(pm);
            KeyPoint[] blobs = new KeyPoint[50];

            blobs = blobdetector.Detect(src);

            output = img.Clone();

            OpenCvSharp.CPlusPlus.Point blobcentre = new OpenCvSharp.CPlusPlus.Point(0,0);
            OpenCvSharp.CPlusPlus.Size sz = new OpenCvSharp.CPlusPlus.Size(10,10);
            for (int i = 0; i < blobs.Length; i++)
            {
                blobcentre.X = (int)blobs[i].Pt.X;
                blobcentre.Y = (int)blobs[i].Pt.Y;

                output.Ellipse(blobcentre, sz, 0, 0, 360, Scalar.Red, 5);

                blobcentroids.Add(blobcentre);
            }
            displayImg(output);

            return output;
        }

        private Mat toEqualize(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat();

            Cv2.EqualizeHist(src, output);

            displayImg(output);
            return output;
        }

        private Mat toAdaptiveThres(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat();

            int sz = Convert.ToInt32(txtboxAdaptiveSize.Text.ToString());
            int index = cboAdaptiveType.SelectedIndex;

            if (autostart)
            {
                sz = 45;
                index = 0;
            }

            AdaptiveThresholdType type = new AdaptiveThresholdType();
            switch (index)
            {
                case 0:
                    type = AdaptiveThresholdType.MeanC;
                    break;
                case 1:
                    type = AdaptiveThresholdType.GaussianC;
                    break;
                default:
                    break;
            }

            Cv2.AdaptiveThreshold(src, output, 255, type, ThresholdType.Binary, sz, 0);

            displayImg(output);
            return output;
        }

        private Mat toErosion(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat();

            int sz = Convert.ToInt32(txtboxErosionSize.Text.ToString());
            int index = cboErosionShape.SelectedIndex;
            StructuringElementShape shape = new StructuringElementShape();

            switch (index)
            {
                case 0:
                    shape = StructuringElementShape.Rect;
                    break;
                case 1:
                    shape = StructuringElementShape.Cross;
                    break;
                case 2:
                    shape = StructuringElementShape.Ellipse;
                    break;
                default:
                    break;
            }

            Mat element = Cv2.GetStructuringElement(shape, new OpenCvSharp.CPlusPlus.Size(2 * sz + 1, 2 * sz + 1));
            Cv2.Erode(src, output, element);

            displayImg(output);

            return output;
        }

        private Mat toDilation(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat();

            int sz = Convert.ToInt32(txtboxDilationSize.Text.ToString());
            int index = cboDilationShape.SelectedIndex;
            StructuringElementShape shape = new StructuringElementShape();

            switch (index)
            {
                case 0:
                    shape = StructuringElementShape.Rect;
                    break;
                case 1:
                    shape = StructuringElementShape.Cross;
                    break;
                case 2:
                    shape = StructuringElementShape.Ellipse;
                    break;
                default:
                    break;
            }

            Mat element = Cv2.GetStructuringElement(shape, new OpenCvSharp.CPlusPlus.Size(2 * sz + 1, 2 * sz + 1));
            Cv2.Dilate(src, output, element);

            displayImg(output);

            return output;
        }

        private Mat toContour(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat();
            Mat[] contours = new Mat[1];
            Mat hierarchy = new Mat();

            Cv2.FindContours(src, out contours, hierarchy, ContourRetrieval.Tree, ContourChain.ApproxSimple);
            output = img.Clone();

            List<Mat> contourspoly = new List<Mat>();
            //Mat[] contourspoly = new Mat[1];
            //contourspoly[0] = new Mat();
            List<Rect> boundRect = new List<Rect>();
            //Rect[] boundRect = new Rect[1];

            for (int i = 0; i < contours.Length; i++)
            {
                contourspoly.Add(new Mat(contours[i].Rows, contours[i].Cols, contours[i].Type(), new Scalar(0)));
                boundRect.Add(new Rect(1,1,1,1));
                Cv2.ApproxPolyDP(contours[i], contourspoly[i], 3, true);
                boundRect[i] = Cv2.BoundingRect(contourspoly[i]);
            }

            for (int i = 0; i < contours.Length; i++)
            {
                Cv2.DrawContours(output, contourspoly, i, new Scalar(255, 0, 0),4);
                output.Rectangle(boundRect[i], new Scalar(0, 255, 0), 4);
                
            }
            
            displayImg(output);

            return output;
        }

        //May need to redo, does not work very well
        private Mat toFloodFill(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat();
            
            Scalar newval = new Scalar(255);
            //int status = 0;
            output = src.Clone();

            for (int i = 0; i < blobcentroids.Count; i++)
            {
                Cv2.FloodFill(output, blobcentroids[i], newval);
                //WriteToLog("flood fill int: " + status.ToString());
            }

            displayImg(output);

            return output;
        }

        private Mat toGabor(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat();
            Mat kernel = new Mat();

            int sz = Convert.ToInt32(txtboxGaborSize.Text.ToString());
            double sigma = Convert.ToDouble(txtboxGaborSigma.Text.ToString());
            double theta = Convert.ToDouble(txtboxGaborTheta.Text.ToString());
            theta = (theta / 180) * Math.PI;
            double lambda = Convert.ToDouble(txtboxGaborLambda.Text.ToString());
            double gamma = Convert.ToDouble(txtboxGaborGamma.Text.ToString());
            double psi = Convert.ToDouble(txtboxGaborPsi.Text.ToString());

            kernel = Cv2.GetGaborKernel(new OpenCvSharp.CPlusPlus.Size(sz, sz), sigma, theta, lambda, gamma, psi, 0);
            //Cv2.ImShow("kernel", kernel);
            Cv2.Filter2D(src, output, -1, kernel);
            
            displayImg(output);

            return output;
        }

        //hidden now
        private Mat toWatershed(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            //src.ConvertTo(src, MatType.CV_8UC3);
            Mat temp = new Mat();
            Cv2.CvtColor(src, temp, ColorConversion.GrayToRgb);
            Mat output = new Mat();
            Mat markers = new Mat(src.Size(), MatType.CV_32SC1);
            markers.SetTo(new Scalar(0));
            output = img.Clone();
            Cv2.Watershed(temp, markers);
            Vec3b color = new Vec3b(0,0,255);
            for (int y = 0; y < markers.Height; y++)
            {
                for (int x = 0; x < markers.Width; x++)
                {
                    int pixel = markers.Get<int>(y, x);
                    if (pixel == -1)
                    {
                        output.Set<Vec3b>(y, x, color);
                    }
                }
            }

            displayImg(output);
            return output;
        }

        //hidden now
        private Mat toDistance(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat();

            Cv2.DistanceTransform(src, output, DistanceType.L2, DistanceMaskSize.Mask5);
            //double min = 0;
            //double max = 0;
            //Cv2.MinMaxLoc(output, out min, out max);
            output = output * 12;
            output.ConvertTo(output, MatType.CV_8UC1);
            //displayImg(output);
            //Cv2.ImShow("Dist", output);
            return output;
        }

        private Mat toAveGabor(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat(src.Size(), MatType.CV_32F);
            Mat kernel = new Mat();
            Mat temp = new Mat();
            output.SetTo(new Scalar(0));
            int totalangle = 180;
            int step = 30;

            int sz = Convert.ToInt32(txtboxGaborSize.Text.ToString());
            double sigma = Convert.ToDouble(txtboxGaborSigma.Text.ToString());
            double lambda = Convert.ToDouble(txtboxGaborLambda.Text.ToString());
            double gamma = Convert.ToDouble(txtboxGaborGamma.Text.ToString());
            double psi = Convert.ToDouble(txtboxGaborPsi.Text.ToString());

            for (int i = 0; i <= totalangle; i += step)
            {
                double theta = ((double)i / 180) * Math.PI;
                kernel = Cv2.GetGaborKernel(new OpenCvSharp.CPlusPlus.Size(sz, sz), sigma, theta, lambda, gamma, psi, 0);
                Cv2.Filter2D(src, temp, -1, kernel);
                Cv2.Accumulate(temp, output,null);
            }
            output = output / ((totalangle/step)+1);
            output.ConvertTo(output, MatType.CV_8UC1);

            displayImg(output);
            kernel.Dispose();
            temp.Dispose();
            return output;
        }

        private Mat toKmeans(Mat src)
        {
            #region UI controls
            int sz = Convert.ToInt32(txtboxKmeansSize.Text.ToString());
            int attempt = Convert.ToInt32(txtboxKmeansAttempt.Text.ToString());
            int maxcnt = Convert.ToInt32(txtboxKmeansMaxcnt.Text.ToString());
            double epsilon = Convert.ToDouble(txtboxKmeansEpsilon.Text.ToString());
            int idxflag = cboKmeansFlag.SelectedIndex;
            int idxterm = cboKmeansTermcrit.SelectedIndex;
            KMeansFlag itemflag = new KMeansFlag();
            CriteriaType itemterm = new CriteriaType();
            switch(idxflag)
            {
                case 0:
                    itemflag = KMeansFlag.PpCenters;
                    break;
                case 1:
                    itemflag = KMeansFlag.RandomCenters;
                    break;
                case 2:
                    itemflag = KMeansFlag.UseInitialLabels;
                    break;
                case 3:
                    itemflag = KMeansFlag.Zero;
                    break;
                default:
                    break;
            }
            switch(idxterm)
            {
                case 0:
                    itemterm = CriteriaType.Epsilon;
                    break;
                case 1:
                    itemterm = CriteriaType.Iteration;
                    break;
                case 2:
                    itemterm = CriteriaType.Number;
                    break;
                default:
                    break;
            }
            TermCriteria term = new TermCriteria(itemterm, maxcnt, epsilon);
            #endregion

            Mat output = new Mat();
            int size = src.Width * src.Height;
            Mat unclustered = src.Reshape(1, size);
            Mat unclusteredF = new Mat();
            Mat clusters = new Mat();
            unclustered.ConvertTo(unclusteredF, MatType.CV_32FC1, 1.0 / 255.0);
            double result = 0;
            result = Cv2.Kmeans(unclusteredF, sz, clusters, term, attempt, itemflag);
            WriteToLog("Kmean Result: " + result.ToString());
            Mat clusteroutput = clusters.Reshape(1, src.Rows);
            clusteroutput = clusteroutput * (255/(sz-1));
            clusteroutput.ConvertTo(output, MatType.CV_8U);

            displayImg(output);
            kmeansglobal = output.Clone();
            #region enable K means layer filter
            cboKmeansFilter.Enabled = true;
            cboKmeansFilter.Items.Clear();
            cboKmeansFilter.Items.Add("All");
            for (int i = 0; i < sz; i++)
            {
                cboKmeansFilter.Items.Add(i.ToString());
            }
            
            #endregion

            return output;
        }

        //Need improvement (WIP)
        private Mat toThinning(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat();

            //--------------------------------------------------------------------------------------------------
            // Erosion Thinning
            //--------------------------------------------------------------------------------------------------
            //Mat skel = new Mat(src.Size(), MatType.CV_8UC1, new Scalar(0));
            //Mat temp = new Mat(src.Size(), MatType.CV_8UC1);
            //Mat element = Cv2.GetStructuringElement(StructuringElementShape.Cross, new OpenCvSharp.CPlusPlus.Size(3, 3));

            //bool done;

            //do
            //{
            //    Cv2.MorphologyEx(src, temp, MorphologyOperation.Open, element);
            //    Cv2.BitwiseNot(temp, temp);
            //    Cv2.BitwiseAnd(src, temp, temp);
            //    Cv2.BitwiseOr(skel, temp, skel);
            //    Cv2.Erode(src, output, element);

            //    double min, max;
            //    Cv2.MinMaxLoc(output, out min, out max);
            //    done = (max <= 0);

            //} while (!done);

            //--------------------------------------------------------------------------------------------------
            // Slow Thinning
            //--------------------------------------------------------------------------------------------------
            Mat thin = new Mat();
            thin = src.Clone();

            thin /= 255;
            Mat prev = new Mat(thin.Size(), MatType.CV_8UC1);
            Mat diff = new Mat();

            do
            {
                //thin = thinningZhangSuenIteration(thin, 0);
                //thin = thinningZhangSuenIteration(thin, 1);
                thin = thinningGuoHallIteration(thin, 0);
                thin = thinningGuoHallIteration(thin, 1);
                Cv2.Absdiff(thin, prev, diff);
                prev = thin.Clone();
            }
            while (Cv2.CountNonZero(diff) > 0);

            output = thin * 255;

            displayImg(output);

            return output;
        }

        private Mat thinningZhangSuenIteration(Mat src, int iter)
        {
            Mat marker = new Mat(src.Size(), MatType.CV_8UC1);
            marker.SetTo(new Scalar(0));

            for (int i = 1; i < (src.Rows - 1); i++)
            {
                for (int j = 1; j < (src.Cols - 1); j++)
                {
                    byte p2 = src.Get<byte>(i - 1, j);
                    byte p3 = src.Get<byte>(i - 1, j + 1);
                    byte p4 = src.Get<byte>(i, j + 1);
                    byte p5 = src.Get<byte>(i + 1, j + 1);
                    byte p6 = src.Get<byte>(i + 1, j);
                    byte p7 = src.Get<byte>(i + 1, j - 1);
                    byte p8 = src.Get<byte>(i, j - 1);
                    byte p9 = src.Get<byte>(i - 1, j - 1);

                    bool A = (p2 == 0 && p3 == 1) & (p3 == 0 && p4 == 1) &
                            (p4 == 0 && p5 == 1) & (p5 == 0 && p6 == 1) &
                            (p6 == 0 && p7 == 1) & (p7 == 0 && p8 == 1) &
                            (p8 == 0 && p9 == 1) & (p9 == 0 && p2 == 1);
                    int B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
                    int m1 = iter == 0 ? (p2 * p4 * p6) : (p2 * p4 * p8);
                    int m2 = iter == 0 ? (p4 * p6 * p8) : (p2 * p6 * p8);

                    if (A && (B >= 2 && B <= 6) && m1 == 0 && m2 == 0)
                    {
                        marker.Set<byte>(i, j, 1);
                    }
                }
            }

            Cv2.BitwiseNot(marker, marker);
            src &= marker;

            return src;
        }

        private Mat thinningGuoHallIteration(Mat src, int iter)
        {
            Mat marker = new Mat(src.Size(), MatType.CV_8UC1);
            marker.SetTo(new Scalar(0));

            for (int i = 1; i < (src.Rows - 1); i++)
            {
                for (int j = 1; j < (src.Cols - 1); j++)
                {
                    byte p2 = src.Get<byte>(i - 1, j);
                    byte p3 = src.Get<byte>(i - 1, j + 1);
                    byte p4 = src.Get<byte>(i, j + 1);
                    byte p5 = src.Get<byte>(i + 1, j + 1);
                    byte p6 = src.Get<byte>(i + 1, j);
                    byte p7 = src.Get<byte>(i + 1, j - 1);
                    byte p8 = src.Get<byte>(i, j - 1);
                    byte p9 = src.Get<byte>(i - 1, j - 1);

                    int C = (~p2 & (p3 | p4)) + (~p4 & (p5 | p6)) +
                            (~p6 & (p7 | p8)) + (~p8 & (p9 | p2));
                    int N1 = (p9 | p2) + (p3 | p4) + (p5 | p6) + (p7 | p8);
                    int N2 = (p2 | p3) + (p4 | p5) + (p6 | p7) + (p8 | p9);
                    int N = N1 < N2 ? N1 : N2;
                    int m = iter == 0 ? ((p6 | p7 | ~p9) & p8) : ((p2 | p3 | ~p5) & p4);

                    if (C == 1 && (N >= 2 && N <= 3) & m == 0)
                    {
                        marker.Set<byte>(i, j, 1);
                    }
                }
            }

            Cv2.BitwiseNot(marker, marker);
            src &= marker;

            return src;
        }

        private Mat toFitGrid(Mat src)
        {
            Mat output = new Mat();

            int sz = Convert.ToInt32(txtboxFitGridSize.Text);
            int width = Convert.ToInt32(txtboxFitGridWidth.Text);
            int height = Convert.ToInt32(txtboxFitGridHeight.Text);
            double ratio = Convert.ToDouble(txtboxFitGridRatio.Text);
            int thickness = Convert.ToInt32(txtboxFitGridThick.Text);
            OpenCvSharp.CPlusPlus.Point p1 = new OpenCvSharp.CPlusPlus.Point();
            OpenCvSharp.CPlusPlus.Point p2 = new OpenCvSharp.CPlusPlus.Point();
            MatchTemplateMethod matchmethod = new MatchTemplateMethod();
            switch (cboFitGridMethod.SelectedIndex)
            {
                case 0:
                    //sqdiff
                    matchmethod = MatchTemplateMethod.SqDiff;
                    break;
                case 1:
                    //sqdiff normed
                    matchmethod = MatchTemplateMethod.SqDiffNormed;
                    break;
                case 2:
                    //ccorr
                    matchmethod = MatchTemplateMethod.CCorr;
                    break;
                case 3:
                    //ccorr normed
                    matchmethod = MatchTemplateMethod.CCorrNormed;
                    break;
                case 4:
                    //ccoeff
                    matchmethod = MatchTemplateMethod.CCoeff;
                    break;
                case 5:
                    //ccoeff normed
                    matchmethod = MatchTemplateMethod.CCoeffNormed;
                    break;
                default:

                    break;
            }

            int gridwidth = 0;
            int gridheight = 0;
            if (fullauto)
            {
                gridwidth = fouriergridwidth;
                gridheight = fouriergridheight;
            }
            else
            {
                gridwidth = (int)(width / ratio);
                gridheight = (int)(height / ratio);
            }
            
            int matwidth = gridwidth * sz;
            int matheight = gridheight * sz;

            Mat grid = new Mat(matheight, matwidth, MatType.CV_8UC1, new Scalar(255));

            for (int y = 0; y < sz; y++)
            {
                for (int x = 0; x < sz; x++)
                {
                    //draw top line
                    p1.X = x * gridwidth;
                    p1.Y = y * gridheight;
                    p2.X = (x + 1) * gridwidth - 1;
                    p2.Y = y * gridheight;
                    grid.Line(p1, p2, new Scalar(0), thickness);

                    //draw bottom line
                    p1.X = x * gridwidth;
                    p1.Y = (y + 1) * gridheight - 1;
                    p2.X = (x + 1) * gridwidth - 1;
                    p2.Y = (y + 1) * gridheight - 1;
                    grid.Line(p1, p2, new Scalar(0), thickness);

                    //draw left line
                    p1.X = x * gridwidth;
                    p1.Y = y * gridheight;
                    p2.X = x * gridwidth;
                    p2.Y = (y + 1) * gridheight - 1;
                    grid.Line(p1, p2, new Scalar(0), thickness);

                    //draw right line
                    p1.X = (x + 1) * gridwidth - 1;
                    p1.Y = y * gridheight;
                    p2.X = (x + 1) * gridwidth - 1;
                    p2.Y = (y + 1) * gridheight - 1;
                    grid.Line(p1, p2, new Scalar(0), thickness);
                }
            }

            //Cv2.ImShow("Grid", grid);
            Mat match = new Mat();
            Cv2.MatchTemplate(src, grid, match, matchmethod);
            Cv2.ImShow("match", match);
            double minVal, maxVal;
            OpenCvSharp.CPlusPlus.Point matchLoc, minLoc, maxLoc, pointloc;
            Cv2.MinMaxLoc(match, out minVal, out maxVal, out minLoc, out maxLoc);
            if ((matchmethod == MatchTemplateMethod.SqDiff) || (matchmethod == MatchTemplateMethod.SqDiffNormed))
            {
                matchLoc = minLoc;
                WriteToLog("grid score: " + (100 - (minVal * 100)).ToString());
            }
            else
            {
                matchLoc = maxLoc;
                WriteToLog("grid score: " + (maxVal * 100).ToString());
            }
            WriteToLog("GridMatch: (" + matchLoc.X.ToString() + "," + matchLoc.Y.ToString() + ")\n");
            pointloc = new OpenCvSharp.CPlusPlus.Point(matchLoc.X + (gridwidth/2), matchLoc.Y + (gridheight/2));

            if (diecornerstage == 1)
            {
                output = diecorner1.Clone();
            }
            else
            {
                output = img.Clone();
            }
            
            Point2f centre = new Point2f(matchLoc.X, matchLoc.Y);
            Point2f ptcentre = new Point2f(pointloc.X, pointloc.Y);
            output.Ellipse(new RotatedRect(centre, new Size2f(12, 12), 0), new Scalar(0, 255, 0), 12);
            output.Ellipse(new RotatedRect(ptcentre, new Size2f(12, 12), 0), new Scalar(0, 0, 255), 12);

            if (autostart || (diecornerstage==1))
            {
                OpenCvSharp.CPlusPlus.Size diesize = new OpenCvSharp.CPlusPlus.Size(gridwidth * autoteachdiesizeratio, gridheight * autoteachdiesizeratio);
                
                OpenCvSharp.CPlusPlus.Point diept = new OpenCvSharp.CPlusPlus.Point(matchLoc.X + (gridwidth * ((1 - autoteachdiesizeratio)/2)), matchLoc.Y + (gridheight * ((1 - autoteachdiesizeratio)/2)));
                Rect die = new Rect(diept, diesize);

                Mat cropped = new Mat();

                if (diecornerstage == 1)
                {
                    cropped = diecorner1[die].Clone();
                }
                else
                {
                    cropped = img[die].Clone();
                }
                
                dietemplate = cropped.Clone();
                Cv2.ImWrite("die.bmp", cropped);
                //Cv2.ImShow("Die", cropped);
                displayDie(cropped);
                float ratioX = (float)DieDisplay.Width / (float)cropped.Width;
                float ratioY = (float)DieDisplay.Height / (float)cropped.Height;
                if (ratioX > ratioY)
                {
                    DieDisplay.Zoom = ratioY;
                }
                else
                {
                    DieDisplay.Zoom = ratioX;
                }
            }

            displayImg(output);

            return output;
        }

        private Mat toTwoGabor(Mat src)
        {
            cboKmeansFilter.Enabled = false;

            Mat output = new Mat(src.Size(), MatType.CV_32FC1);
            Mat kernel = new Mat();
            Mat temp = new Mat();
            output.SetTo(new Scalar(0));

            int sz = Convert.ToInt32(txtboxGaborSize.Text.ToString());
            double sigma = Convert.ToDouble(txtboxGaborSigma.Text.ToString());
            double lambda = Convert.ToDouble(txtboxGaborLambda.Text.ToString());
            double gamma = Convert.ToDouble(txtboxGaborGamma.Text.ToString());
            double psi = Convert.ToDouble(txtboxGaborPsi.Text.ToString());

            if (autostart)
            {
                sz = 31;
                sigma = 5;
                lambda = 10;
                gamma = 0.1;
                psi = 0;
            }

            kernel = Cv2.GetGaborKernel(new OpenCvSharp.CPlusPlus.Size(sz, sz), sigma, (double)0, lambda, gamma, psi, 0);
            Cv2.Filter2D(src, temp, -1, kernel);
            Cv2.Accumulate(temp, output, null);
            kernel = Cv2.GetGaborKernel(new OpenCvSharp.CPlusPlus.Size(sz, sz), sigma, (double)(Math.PI / 2), lambda, gamma, psi, 0);
            Cv2.Filter2D(src, temp, -1, kernel);
            Cv2.Accumulate(temp, output, null);
            
            output = output / 2;
            output.ConvertTo(output, MatType.CV_8UC1);

            displayImg(output);
            kernel.Dispose();
            temp.Dispose();
            return output;
        }

        private Mat toSharpen(Mat src)
        {
            Mat output = new Mat();
            int sz = Convert.ToInt32(txtboxGaussianSize.Text.ToString());
            int sigma = Convert.ToInt32(txtboxGaussianSigma.Text.ToString());

            if (autostart)
            {
                sz = 35;
                sigma = 0;
            }

            Cv2.GaussianBlur(src, output, new OpenCvSharp.CPlusPlus.Size(sz, sz), sigma);
            Cv2.AddWeighted(src, 1.5, output, -0.5, 0, output);

            displayImg(output);

            return output;
        }

        private void toMatchDieTemplate()
        {
            Mat output = new Mat();
            Mat gray = new Mat();
            Mat BW = new Mat();
            Mat graytemplate = new Mat();
            Mat BWtemplate = new Mat();
            Mat match = new Mat();

            #region UI handling
            int width = Convert.ToInt32(txtboxFitGridWidth.Text);
            int height = Convert.ToInt32(txtboxFitGridHeight.Text);
            double ratio = Convert.ToDouble(txtboxFitGridRatio.Text);
            int gridwidth = 0;
            int gridheight = 0;
            if (fullauto)
            {
                gridwidth = fouriergridwidth;
                gridheight = fouriergridheight;
            }
            else
            {
                gridwidth = (int)(width / ratio);
                gridheight = (int)(height / ratio);
            }
            int diematchnum = Convert.ToInt32(txtboxDieMatchNum.Text);
            int minscore = Convert.ToInt32(txtboxMinScore.Text);
            MatchTemplateMethod matchmethod = new MatchTemplateMethod();
            switch (cboFitGridMethod.SelectedIndex)
            {
                case 0:
                    //sqdiff
                    matchmethod = MatchTemplateMethod.SqDiff;
                    break;
                case 1:
                    //sqdiff normed
                    matchmethod = MatchTemplateMethod.SqDiffNormed;
                    break;
                case 2:
                    //ccorr
                    matchmethod = MatchTemplateMethod.CCorr;
                    break;
                case 3:
                    //ccorr normed
                    matchmethod = MatchTemplateMethod.CCorrNormed;
                    break;
                case 4:
                    //ccoeff
                    matchmethod = MatchTemplateMethod.CCoeff;
                    break;
                case 5:
                    //ccoeff normed
                    matchmethod = MatchTemplateMethod.CCoeffNormed;
                    break;
                default:
                    break;
            }
            #endregion

            if (diecornerstage == 1)
            {
                Cv2.CvtColor(diecorner1, gray, ColorConversion.BgrToGray);
            }
            else if (diecornerstage == 2)
            {
                Cv2.CvtColor(diecorner2, gray, ColorConversion.BgrToGray);
            }
            else
            {
                Cv2.CvtColor(img, gray, ColorConversion.BgrToGray);
            }

            if (diecornerstage == 2)
            {
                //Cv2.CvtColor(diecornermean, graytemplate, ColorConversion.BgrToGray);
                graytemplate = diecornermean.Clone();
            }
            else
            {
                Cv2.CvtColor(dietemplate, graytemplate, ColorConversion.BgrToGray);
            }

            //please note that adaptive threshold size for both main img and die template is fixed at 21

            if (diecornerstage == 2)
            {
                Cv2.AdaptiveThreshold(gray, BW, 255, AdaptiveThresholdType.MeanC, ThresholdType.Binary, 11, 0);
                Cv2.AdaptiveThreshold(graytemplate, BWtemplate, 255, AdaptiveThresholdType.MeanC, ThresholdType.Binary, 11, 0);
            }
            else
            {
                Cv2.AdaptiveThreshold(gray, BW, 255, AdaptiveThresholdType.MeanC, ThresholdType.Binary, 21, 0);
                Cv2.AdaptiveThreshold(graytemplate, BWtemplate, 255, AdaptiveThresholdType.MeanC, ThresholdType.Binary, 21, 0);
            }
            
            Cv2.MatchTemplate(BW, BWtemplate, match, matchmethod);

            double minVal, maxVal;
            OpenCvSharp.CPlusPlus.Point matchLoc, minLoc, maxLoc;
            totalmatch = 0;
            matchpos.Clear();
            colval.Clear();
            rowval.Clear();
            colnum = 0;
            rownum = 0;
            //Calculate matched point coordinates
            #region Calculate matched points

            if (diematchmode || (diecornerstage!=0))
            {
                //minscore
                double score = 0;
                if (diecornerstage == 2)
                {
                    minscore = 40;
                }

                do
                {
                    Cv2.MinMaxLoc(match, out minVal, out maxVal, out minLoc, out maxLoc);
                    if ((matchmethod == MatchTemplateMethod.SqDiff) || (matchmethod == MatchTemplateMethod.SqDiffNormed))
                    {
                        matchLoc = minLoc;
                        score = 100 - (minVal * 100);
                        //WriteToLog("score: " + score.ToString());
                    }
                    else
                    {
                        matchLoc = maxLoc;
                        score = maxVal * 100;
                        //WriteToLog("score: " + score.ToString());
                    }

                    //WriteToLog("match pos: " + matchLoc.X.ToString() + "," + matchLoc.Y.ToString());
                    if (score >= minscore)
                    {
                        matchpos.Add(new OpenCvSharp.CPlusPlus.Point((matchLoc.X + (gridwidth / 2)), (matchLoc.Y + (gridheight / 2))));
                        totalmatch++;
                    }

                    int maskwidth = (int)(gridwidth / 3);
                    int maskheight = (int)(gridheight / 3);

                    matchLoc.X = matchLoc.X - (maskwidth / 2);
                    matchLoc.Y = matchLoc.Y - (maskheight / 2);

                    if ((matchLoc.X < 0) && (matchLoc.Y < 0))
                    {
                        matchLoc.X = 0;
                        matchLoc.Y = 0;
                    }
                    else if (matchLoc.X < 0)
                    {
                        matchLoc.X = 0;
                    }
                    else if (matchLoc.Y < 0)
                    {
                        matchLoc.Y = 0;
                    }

                    if (((matchLoc.X + maskwidth) > match.Width) && ((matchLoc.Y + maskheight) > match.Height))
                    {
                        maskwidth = maskwidth - (matchLoc.X + maskwidth - match.Width);
                        maskheight = maskheight - (matchLoc.Y + maskheight - match.Height);
                    }
                    else if ((matchLoc.X + maskwidth) > match.Width)
                    {
                        maskwidth = maskwidth - (matchLoc.X + maskwidth - match.Width);
                    }
                    else if ((matchLoc.Y + maskheight) > match.Height)
                    {
                        maskheight = maskheight - (matchLoc.Y + maskheight - match.Height);
                    }

                    Rect maskROI = new Rect(matchLoc.X, matchLoc.Y, maskwidth, maskheight);
                    OpenCvSharp.CPlusPlus.Size masksize = new OpenCvSharp.CPlusPlus.Size(maskwidth, maskheight);
                    Mat mask = new Mat(masksize, match.Type());

                    if ((matchmethod == MatchTemplateMethod.SqDiff) || (matchmethod == MatchTemplateMethod.SqDiffNormed))
                        mask.SetTo(new Scalar(1));
                    else
                        mask.SetTo(new Scalar(0));

                    match[maskROI] = mask;
                }
                while (score >= minscore);
            }
            else
            {
                if (fullauto)
                {
                    totalmatch = fourierdiematchnum;
                }
                else
                {
                    totalmatch = diematchnum;
                }
                

                for (int count = 0; count < totalmatch; count++)
                {
                    Cv2.MinMaxLoc(match, out minVal, out maxVal, out minLoc, out maxLoc);
                    if ((matchmethod == MatchTemplateMethod.SqDiff) || (matchmethod == MatchTemplateMethod.SqDiffNormed))
                    {
                        matchLoc = minLoc;
                        //WriteToLog("score: " + (100 - (minVal * 100)).ToString());
                    }
                    else
                    {
                        matchLoc = maxLoc;
                        //WriteToLog("score: " + (maxVal * 100).ToString());
                    }
                    //WriteToLog("match pos: " + matchLoc.X.ToString() + "," + matchLoc.Y.ToString());

                    matchpos.Add(new OpenCvSharp.CPlusPlus.Point((matchLoc.X + (gridwidth / 2)), (matchLoc.Y + (gridheight / 2))));

                    int maskwidth = (int)(gridwidth / 3);
                    int maskheight = (int)(gridheight / 3);

                    matchLoc.X = matchLoc.X - (maskwidth / 2);
                    matchLoc.Y = matchLoc.Y - (maskheight / 2);

                    if ((matchLoc.X < 0) && (matchLoc.Y < 0))
                    {
                        matchLoc.X = 0;
                        matchLoc.Y = 0;
                    }
                    else if (matchLoc.X < 0)
                    {
                        matchLoc.X = 0;
                    }
                    else if (matchLoc.Y < 0)
                    {
                        matchLoc.Y = 0;
                    }

                    if (((matchLoc.X + maskwidth) > match.Width) && ((matchLoc.Y + maskheight) > match.Height))
                    {
                        maskwidth = maskwidth - (matchLoc.X + maskwidth - match.Width);
                        maskheight = maskheight - (matchLoc.Y + maskheight - match.Height);
                    }
                    else if ((matchLoc.X + maskwidth) > match.Width)
                    {
                        maskwidth = maskwidth - (matchLoc.X + maskwidth - match.Width);
                    }
                    else if ((matchLoc.Y + maskheight) > match.Height)
                    {
                        maskheight = maskheight - (matchLoc.Y + maskheight - match.Height);
                    }

                    Rect maskROI = new Rect(matchLoc.X, matchLoc.Y, maskwidth, maskheight);
                    OpenCvSharp.CPlusPlus.Size masksize = new OpenCvSharp.CPlusPlus.Size(maskwidth, maskheight);
                    Mat mask = new Mat(masksize, match.Type());

                    if ((matchmethod == MatchTemplateMethod.SqDiff) || (matchmethod == MatchTemplateMethod.SqDiffNormed))
                        mask.SetTo(new Scalar(1));
                    else
                        mask.SetTo(new Scalar(0));

                    match[maskROI] = mask;
                }
            }
            WriteToLog("total match: " + totalmatch.ToString());
            
            #endregion

            //plot vertical lines
            #region plot vertical line
                OpenCvSharp.CPlusPlus.Point firstpt = new OpenCvSharp.CPlusPlus.Point(0, 0);
                OpenCvSharp.CPlusPlus.Point[] pos = new OpenCvSharp.CPlusPlus.Point[40];
                List<OpenCvSharp.CPlusPlus.Point> matchpos_buffer = new List<OpenCvSharp.CPlusPlus.Point>(matchpos);
                double[] vertiangle = new double[50];
                vertiangle.Initialize();
                int ptcount = 0;
                int linecount = 0;
                int verticount = 0;
                colnum = 0;
                rownum = 0;
                while (ptcount < totalmatch)
                {
                    linecount = 0;
                    for (int q = 0; q < totalmatch; q++)
                    {
                        if (matchpos_buffer[q].X >= 0)
                        {
                            firstpt = matchpos_buffer[q];
                            colnum++;
                            colval.Add(firstpt.X);
                            break;
                        }
                    }

                    for (int q = 0; q < totalmatch; q++)
                    {
                        if (matchpos_buffer[q].X >= 0)
                        {
                            if ((matchpos_buffer[q].X > (firstpt.X - Xdiff)) && (matchpos_buffer[q].X < (firstpt.X + Xdiff)))
                            {
                                pos[linecount] = matchpos_buffer[q];
                                linecount++;
                                ptcount++;
                                matchpos_buffer[q] = new OpenCvSharp.CPlusPlus.Point(-100, -100);
                            }
                        }
                    }

                    if (linecount > 3)
                    {
                        //calculate best fit line
                        double meanX = 0;
                        double meanY = 0;
                        for (int q = 0; q < linecount; q++)
                        {
                            meanX += (double)pos[q].X;
                            meanY += (double)pos[q].Y;
                        }
                        meanX = meanX / (double)linecount;
                        meanY = meanY / (double)linecount;
                        double top = 0;
                        double btm = 0;
                        for (int q = 0; q < linecount; q++)
                        {
                            top += ((double)pos[q].X - meanX) * ((double)pos[q].Y - meanY);
                            btm += Math.Pow(((double)pos[q].X - meanX), 2);
                        }
                        double m = top / btm;
                        vertiangle[verticount] = (Math.Atan(m) / Math.PI) * 180;
                        verticount++;
                    }
                }
                #endregion

            //plot horizontal lines
            #region plot horizontal lines
                firstpt = new OpenCvSharp.CPlusPlus.Point(0, 0);
                pos = new OpenCvSharp.CPlusPlus.Point[40];
                matchpos_buffer = new List<OpenCvSharp.CPlusPlus.Point>(matchpos);
                double[] horiangle = new double[50];
                horiangle.Initialize();
                ptcount = 0;
                linecount = 0;
                int horicount = 0;
                //group coordinates into lines
                while (ptcount < totalmatch)
                {
                    linecount = 0;
                    for (int q = 0; q < totalmatch; q++)
                    {
                        if (matchpos_buffer[q].X >= 0)
                        {
                            firstpt = matchpos_buffer[q];
                            rownum++;
                            rowval.Add(firstpt.Y);
                            break;
                        }
                    }

                    for (int q = 0; q < totalmatch; q++)
                    {
                        if (matchpos_buffer[q].X >= 0)
                        {
                            if ((matchpos_buffer[q].Y > (firstpt.Y - Ydiff)) && (matchpos_buffer[q].Y < (firstpt.Y + Ydiff)))
                            {
                                pos[linecount] = matchpos_buffer[q];
                                linecount++;
                                ptcount++;
                                matchpos_buffer[q] = new OpenCvSharp.CPlusPlus.Point(-100, -100);
                            }
                        }
                    }

                    if (linecount > 3)
                    {
                        //calculate best fit line
                        double meanX = 0;
                        double meanY = 0;
                        for (int q = 0; q < linecount; q++)
                        {
                            meanX += (double)pos[q].X;
                            meanY += (double)pos[q].Y;
                            //WriteToLog("horipos: " + pos[q].X.ToString() + "," + pos[q].Y.ToString());
                        }
                        meanX = meanX / (double)linecount;
                        meanY = meanY / (double)linecount;
                        double top = 0;
                        double btm = 0;
                        for (int q = 0; q < linecount; q++)
                        {
                            top += ((double)pos[q].X - meanX) * ((double)pos[q].Y - meanY);
                            btm += Math.Pow(((double)pos[q].X - meanX), 2);
                        }

                        double m = top / btm;
                        horiangle[horicount] = (Math.Atan(m) / Math.PI) * 180;
                        horicount++;
                        //WriteToLog("hori grad: " + m.ToString());
                        //WriteToLog("hori angle: " + horiangle[horicount - 1].ToString() + "\n");
                    }
                }
                #endregion

            //Calculate Final angle offset
            #region calculate final angle
                double vangle = 0;
                for (int q = 0; q < verticount; q++)
                {
                    vangle += vertiangle[q];
                }
                vangle = vangle / verticount;
                WriteToLog("final verti angle: " + vangle.ToString());

                double hangle = 0;
                for (int q = 0; q < horicount; q++)
                {
                    hangle += horiangle[q];
                }
                hangle = hangle / horicount;
                WriteToLog("final hori angle: " + hangle.ToString());

                if (vangle <= 0)
                {
                    vangle += 90;
                }
                else
                {
                    vangle -= 90;
                }
                if (double.IsNaN(vangle))
                {
                    vangle = 0;
                }
                if (double.IsNaN(hangle))
                {
                    hangle = 0;
                }
                double angle_offset = 0;
                angle_offset = (hangle + vangle) / 2;
                WriteToLog("Final angle offset: " + angle_offset.ToString() + "\n");
                #endregion

            WriteToLog("Col: " + colnum.ToString() + ", Row: " + rownum.ToString());

            if (diecornerstage == 2)
            {
                output = diecorner2.Clone();
            }
            else if (diecornerstage == 1)
            {
                output = diecorner1.Clone();
            }
            else
            {
                output = img.Clone();
            }

            for (int i = 0; i < totalmatch; i++)
            {
                Point2f diecenterposition = new Point2f(matchpos[i].X, matchpos[i].Y);
                RotatedRect dierect = new RotatedRect(diecenterposition, new Size2f(12, 12), 0);
                output.Ellipse(dierect, new Scalar(0, 0, 255), 8);
            }
            
            displayImg(output);

        }

        private Mat toStatsMean(Mat src)
        {
            Mat mean = new Mat();
            Mat cropped = new Mat();
            Mat gray = new Mat();

            gray = toGrayscale(src);

            int width = Convert.ToInt32(txtboxFitGridWidth.Text);
            int height = Convert.ToInt32(txtboxFitGridHeight.Text);
            double ratio = Convert.ToDouble(txtboxFitGridRatio.Text);
            int gridwidth = (int)(width / ratio);
            int gridheight = (int)(height / ratio);
            
            OpenCvSharp.CPlusPlus.Size diesz = new OpenCvSharp.CPlusPlus.Size(gridwidth, gridheight);
            Mat add = new Mat(diesz, MatType.CV_32FC1);
            add.SetTo(new Scalar(0));

            for (int i = 0; i < totalmatch; i++)
            {
                OpenCvSharp.CPlusPlus.Point diept = new OpenCvSharp.CPlusPlus.Point();
                diept.X = matchpos[i].X - (gridwidth / 2);
                diept.Y = matchpos[i].Y - (gridheight / 2);
 
                Rect dierect = new Rect(diept, diesz);

                cropped = gray[dierect].Clone();

                Cv2.Accumulate(cropped, add, null);
            }

            add = add / totalmatch;
            add.ConvertTo(mean, MatType.CV_8UC1);

            displayMean(mean);
            float ratioX = (float)MeanDisplay.Width / (float)mean.Width;
            float ratioY = (float)MeanDisplay.Height / (float)mean.Height;
            if (ratioX > ratioY)
            {
                MeanDisplay.Zoom = ratioY;
            }
            else
            {
                MeanDisplay.Zoom = ratioX;
            }

            cropped.Dispose();
            gray.Dispose();

            return mean;
        }

        private Mat toStatsStdDev(Mat src, Mat mean)
        {
            Mat SD = new Mat();
            Mat cropped = new Mat();
            Mat gray = new Mat();
            
            gray = toGrayscale(src);

            int width = Convert.ToInt32(txtboxFitGridWidth.Text);
            int height = Convert.ToInt32(txtboxFitGridHeight.Text);
            double ratio = Convert.ToDouble(txtboxFitGridRatio.Text);
            int gridwidth = (int)(width / ratio);
            int gridheight = (int)(height / ratio);
            
            OpenCvSharp.CPlusPlus.Size diesz = new OpenCvSharp.CPlusPlus.Size(gridwidth, gridheight);
            Mat add = new Mat(diesz, MatType.CV_32FC1);
            add.SetTo(new Scalar(0));
            Mat temp = new Mat(diesz, MatType.CV_32FC1);

            for (int i = 0; i < totalmatch; i++)
            {
                OpenCvSharp.CPlusPlus.Point diept = new OpenCvSharp.CPlusPlus.Point();
                diept.X = matchpos[i].X - (gridwidth / 2);
                diept.Y = matchpos[i].Y - (gridheight / 2);

                Rect dierect = new Rect(diept, diesz);

                cropped = gray[dierect].Clone();
                //temp = cropped - mean;
                //temp = temp * temp;

                Cv2.Absdiff(cropped, mean, temp);
                //Cv2.Abs(temp);
                Cv2.Pow(temp, 2, temp);

                Cv2.Accumulate(temp, add, null);
            }

            Cv2.Divide((double)totalmatch, add, add);
            Cv2.Pow(add, 0.5, SD);
            SD = SD * 255;
            SD.ConvertTo(SD, MatType.CV_8UC1);
            //Cv2.ImShow("SD", SD);

            displaySD(SD);
            float ratioX = (float)SDDisplay.Width / (float)SD.Width;
            float ratioY = (float)SDDisplay.Height / (float)SD.Height;
            if (ratioX > ratioY)
            {
                SDDisplay.Zoom = ratioY;
            }
            else
            {
                SDDisplay.Zoom = ratioX;
            }

            return SD;
        }

        private void toStatsZscore(Mat src, Mat mean, Mat SD)
        {
            Mat gray = new Mat();
            Mat diff = new Mat();
            Mat cropped = new Mat();
            Mat output = new Mat();

            gray = toGrayscale(src);

            if (diecornerstage == 2)
            {
                output = new Mat(gray.Size(), MatType.CV_8UC1,new Scalar(0));
                
            }
            else
            {
                output = gray.Clone();
            }
            

            int width = Convert.ToInt32(txtboxFitGridWidth.Text);
            int height = Convert.ToInt32(txtboxFitGridHeight.Text);
            double ratio = Convert.ToDouble(txtboxFitGridRatio.Text);
            int gridwidth = (int)(width / ratio);
            int gridheight = (int)(height / ratio);

            OpenCvSharp.CPlusPlus.Size diesz = new OpenCvSharp.CPlusPlus.Size(gridwidth, gridheight);
            Mat Zimg;

            for (int i = 0; i < totalmatch; i++)
            {
                Zimg = new Mat(diesz, MatType.CV_32FC1);
                OpenCvSharp.CPlusPlus.Point diept = new OpenCvSharp.CPlusPlus.Point();
                diept.X = matchpos[i].X - (gridwidth / 2);
                diept.Y = matchpos[i].Y - (gridheight / 2);

                Rect dierect = new Rect(diept, diesz);

                cropped = gray[dierect].Clone();

                Cv2.Absdiff(cropped, mean, diff);
                Cv2.Divide(diff, SD, Zimg);
                Zimg = Zimg * (255 / Zscoreunit);
                Zimg.ConvertTo(Zimg, MatType.CV_8UC1);

                //find die z score mean
                Scalar m = Cv2.Mean(Zimg);
                //WriteToLog(m[0].ToString());
                if (m[0] > 15)
                {
                    //Zimg.SetTo(new Scalar(255));
                }
                //end

                //find die highest point val
                double minVal, maxVal;
                Cv2.MinMaxLoc(Zimg, out minVal, out maxVal);
                if (maxVal > 200)
                {
                    //Zimg.SetTo(new Scalar(255));
                }
                //end

                if (diecornerstage == 2)
                {
                    Cv2.Threshold(Zimg, Zimg, ((double)(255 / Zscoreunit) * Zscorethres), 255, ThresholdType.Binary);
                }
                
                //Cv2.Threshold(Zimg, Zimg, ((double)(255 / Zscoreunit) * Zscorethres), 255, ThresholdType.Binary);
                Mat submat = output.SubMat(dierect);
                Zimg.CopyTo(submat);
            }
            
            if (diecornerstage == 2)
            {
                float Minarea = 150;
                float Maxarea = 10000;

                SimpleBlobDetector.Params pm = new SimpleBlobDetector.Params();
                pm.MinDistBetweenBlobs = 10.0f;
                pm.FilterByInertia = false;
                pm.FilterByConvexity = false;
                pm.FilterByColor = false;
                pm.FilterByCircularity = false;
                pm.FilterByArea = true;
                pm.MinArea = Minarea;
                pm.MaxArea = Maxarea;

                SimpleBlobDetector blobdetector = new SimpleBlobDetector(pm);
                KeyPoint[] blobs = new KeyPoint[50];

                blobs = blobdetector.Detect(output);

                Cv2.CvtColor(output, output, ColorConversion.GrayToBgr);
                
                OpenCvSharp.CPlusPlus.Point blobcentre = new OpenCvSharp.CPlusPlus.Point(0, 0);
                OpenCvSharp.CPlusPlus.Size sz = new OpenCvSharp.CPlusPlus.Size(5, 5);
                for (int i = 0; i < blobs.Length; i++)
                {
                    blobcentre.X = (int)blobs[i].Pt.X;
                    blobcentre.Y = (int)blobs[i].Pt.Y;

                    output.Ellipse(blobcentre, sz, 0, 0, 360, Scalar.Red, 5);

                    blobcentroids.Add(blobcentre);
                }
                displayImg(output);
                
                List<int> removalidx = new List<int>();
                List<int> remove = new List<int>();

                for (int blobidx = 0; blobidx < blobs.Length; blobidx++)
                {
                    double shortestdist = 10000;
                    int shortestidx = 0;

                    for (int matchidx = 0; matchidx < matchpos.Count; matchidx++)
                    {
                        int x = Math.Abs(matchpos[matchidx].X - (int)blobs[blobidx].Pt.X);
                        int y = Math.Abs(matchpos[matchidx].Y - (int)blobs[blobidx].Pt.Y);
                        double num = (x ^ 2) + (y ^ 2);
                        double dist = Math.Sqrt(num);

                        if (dist < shortestdist)
                        {
                            shortestdist = dist;
                            shortestidx = matchidx;
                        }
                    }
                    removalidx.Add(shortestidx);
                }

                remove = removalidx.Distinct<int>().ToList<int>();
                removalidx.Clear();
                remove.Sort();

                for (int i = 0; i < remove.Count; i++)
                {
                    matchpos.RemoveAt(remove[i]-i);
                    WriteToLog("Removed: " + remove[i].ToString());
                }
                output = src.Clone();
                for (int i = 0; i < (totalmatch-remove.Count); i++)
                {
                    Point2f diecenterposition = new Point2f(matchpos[i].X, matchpos[i].Y);
                    RotatedRect dierect = new RotatedRect(diecenterposition, new Size2f(12, 12), 0);
                    output.Ellipse(dierect, new Scalar(0, 0, 255), 8);
                }
            }

            displayImg(output);
        }

        private void toCreate2DMap()
        {
            diemap = new int[rownum, colnum];
            Array.Clear(diemap, 0, diemap.Length);
            colval.Sort();
            rowval.Sort();
            //WriteToLog("Cols Val:");
            //for (int i = 0; i < colnum; i++)
            //{
            //    WriteToLog(colval[i].ToString());
            //}

            //WriteToLog("\nRows Val:");
            //for (int i = 0; i < rownum; i++)
            //{
            //    WriteToLog(rowval[i].ToString());
            //}
            totalmatch = matchpos.Count;
            for (int y = 0; y < rownum; y++)
            {
                for (int x = 0; x < colnum; x++)
                {
                    for (int i = 0; i < totalmatch; i++)
                    {
                        if ((matchpos[i].X > (colval[x] - Xdiff)) && (matchpos[i].X < (colval[x] + Xdiff)))
                        {
                            if ((matchpos[i].Y > (rowval[y] - Ydiff)) && (matchpos[i].Y < (rowval[y] + Ydiff)))
                            {
                                diemap[y, x] = 1;
                            }
                        }
                    }
                }
            }

            WriteToLog("\n2D map:");
            for (int i = 0; i < rownum; i++)
            {
                string temp = "";
                for (int j = 0; j < colnum; j++)
                {
                    temp = temp + diemap[i, j].ToString() + ", ";
                }
                WriteToLog(temp);
            }
        }

        //WIP
        private Point2f findFourierDist(Mat src)
        {
            Point2f output = new Point2f();
            double minVal, maxVal;
            OpenCvSharp.CPlusPlus.Point minLoc, maxLoc;
            List<OpenCvSharp.CPlusPlus.Point> ptlist = new List<OpenCvSharp.CPlusPlus.Point>();
            Point2f diecenterposition;
            RotatedRect dierect;
            Mat extractedwidth = new Mat();
            Mat extractedheight = new Mat();
            double Xdist = 1000;
            double Ydist = 1000;
            double dist = 0;

            do
            {
                Cv2.MinMaxLoc(src, out minVal, out maxVal, out minLoc, out maxLoc);
                diecenterposition = new Point2f(maxLoc.X, maxLoc.Y);
                dierect = new RotatedRect(diecenterposition, new Size2f(5, 5), 0);
                src.Ellipse(dierect, new Scalar(0, 0, 0), 5);
                ptlist.Add(maxLoc);
            } while (maxVal > 180);
            WriteToLog("count: " + ptlist.Count.ToString());
            for (int i = 1; i < ptlist.Count; i++)
            {
                dist = Math.Abs(ptlist[i].X - ptlist[0].X);
                if (dist != 0)
                    if (dist < Xdist)
                        Xdist = dist;

                dist = Math.Abs(ptlist[i].Y - ptlist[0].Y);
                if (dist != 0)
                    if (dist < Ydist)
                        Ydist = dist;
            }

            ////extract width strip
            //Cv2.MinMaxLoc(src, out minVal, out maxVal, out minLoc, out maxLoc);
            //Rect extractwidthrect = new Rect(new OpenCvSharp.CPlusPlus.Point(0, (maxLoc.Y - 5)), new OpenCvSharp.CPlusPlus.Size(src.Width, 10));
            //extractedwidth = src[extractwidthrect].Clone();
            
            //for (int i = 0; i < 3; i++)
            //{
            //    Cv2.MinMaxLoc(extractedwidth, out minVal, out maxVal, out minLoc, out maxLoc);
            //    diecenterposition = new Point2f(maxLoc.X, maxLoc.Y);
            //    dierect = new RotatedRect(diecenterposition, new Size2f(4, 4), 0);
            //    extractedwidth.Ellipse(dierect, new Scalar(0, 0, 0), 4);
            //    widthpt[i] = maxLoc;
            //    //WriteToLog(i.ToString() + ": " + maxLoc.X.ToString() + ", " + maxLoc.Y.ToString());
            //}
            //Cv2.ImShow("width", extractedwidth);
            //sumX += Math.Abs(widthpt[1].X - widthpt[0].X);
            //sumX += Math.Abs(widthpt[2].X - widthpt[0].X);
            //sumX /= 2;
            //output.X = (float)sumX;


            ////extract height strip
            //Cv2.MinMaxLoc(src, out minVal, out maxVal, out minLoc, out maxLoc);
            //Rect extractheightrect = new Rect(new OpenCvSharp.CPlusPlus.Point((maxLoc.X - 5), 0), new OpenCvSharp.CPlusPlus.Size(10, src.Height));
            //extractedheight = src[extractheightrect].Clone();
            
            //for (int i = 0; i < 3; i++)
            //{
            //    Cv2.MinMaxLoc(extractedheight, out minVal, out maxVal, out minLoc, out maxLoc);
            //    diecenterposition = new Point2f(maxLoc.X, maxLoc.Y);
            //    dierect = new RotatedRect(diecenterposition, new Size2f(4, 4), 0);
            //    extractedheight.Ellipse(dierect, new Scalar(0, 0, 0), 4);
            //    heightpt[i] = maxLoc;
            //    //WriteToLog(i.ToString() + ": " + maxLoc.X.ToString() + ", " + maxLoc.Y.ToString());
            //}
            //Cv2.ImShow("height", extractedheight);
            //sumY += Math.Abs(heightpt[1].Y - heightpt[0].Y);
            //sumY += Math.Abs(heightpt[2].Y - heightpt[0].Y);
            //sumY /= 2;
            //output.Y = (float)sumY;

            //Mat submat = src.SubMat(extractwidthrect);
            //extractedwidth.CopyTo(submat);
            //submat = src.SubMat(extractheightrect);
            //extractedheight.CopyTo(submat);

            output.X = (float)Xdist;
            output.Y = (float)Ydist;
            WriteToLog("Xdist: " + Xdist.ToString());
            WriteToLog("Ydist: " + Ydist.ToString());

            int Xcount = (int)Xdist - 2;
            int Ycount = (int)Ydist - 2;

            fourierdiematchnum = Xcount * Ycount;
            WriteToLog("Die Match Num: " + fourierdiematchnum.ToString());

            displayImg(src);

            return output;
        }

        private Point2f calculateDieSize(Point2f src)
        {
            Point2f output = new Point2f();

            output.X = (float)img.Width / (float)src.X;
            output.Y = (float)img.Height / (float)src.Y;

            WriteToLog("Die Size: " + output.X.ToString() + ", " + output.Y.ToString());

            return output;
        }

        private Mat ExtractMatchingModel(Mat src, int width, int height)
        {
            Mat output = new Mat();

            Rect modelrect = new Rect(0, 0, width, height);
            output = src[modelrect].Clone();
            Cv2.ImShow("model", output);
            return output;
        }

        private double MatchDie2(Mat src, Mat model, int width, int height)
        {
            Mat gray = new Mat();
            Mat BW = new Mat();
            Mat graymodel = new Mat();
            Mat BWmodel = new Mat();
            Mat match = new Mat();

            Cv2.CvtColor(src, gray, ColorConversion.BgrToGray);
            Cv2.CvtColor(model, graymodel, ColorConversion.BgrToGray);
            Cv2.AdaptiveThreshold(gray, BW, 255, AdaptiveThresholdType.MeanC, ThresholdType.Binary, 21, 0);
            Cv2.AdaptiveThreshold(graymodel, BWmodel, 255, AdaptiveThresholdType.MeanC, ThresholdType.Binary, 21, 0);

            Cv2.MatchTemplate(BW, BWmodel, match, MatchTemplateMethod.SqDiffNormed);

            int XCount = (int)(src.Width / width) - 1;
            int YCount = (int)(src.Height / height) - 1;
            int totalmatch = XCount * YCount;
            WriteToLog("X: " + XCount.ToString() + " Y: " + YCount.ToString() + " Cnt: " + totalmatch.ToString());
            OpenCvSharp.CPlusPlus.Point minLoc, maxLoc;
            double minVal, maxVal;
            matchpos.Clear();

            //find match points
            #region Find Match Pts

            for (int count = 0; count < totalmatch; count++)
            {
                Cv2.MinMaxLoc(match, out minVal, out maxVal, out minLoc, out maxLoc);
                //matchLoc = minLoc;

                matchpos.Add(minLoc);
                
                int maskwidth = (int)(width / 3);
                int maskheight = (int)(height / 3);

                if ((minLoc.X < 0) && (minLoc.Y < 0))
                {
                    minLoc.X = 0;
                    minLoc.Y = 0;
                }
                else if (minLoc.X < 0)
                {
                    minLoc.X = 0;
                }
                else if (minLoc.Y < 0)
                {
                    minLoc.Y = 0;
                }

                if (((minLoc.X + maskwidth) > match.Width) && ((minLoc.Y + maskheight) > match.Height))
                {
                    maskwidth = maskwidth - (minLoc.X + maskwidth - match.Width);
                    maskheight = maskheight - (minLoc.Y + maskheight - match.Height);
                }
                else if ((minLoc.X + maskwidth) > match.Width)
                {
                    maskwidth = maskwidth - (minLoc.X + maskwidth - match.Width);
                }
                else if ((minLoc.Y + maskheight) > match.Height)
                {
                    maskheight = maskheight - (minLoc.Y + maskheight - match.Height);
                }

                Rect maskROI = new Rect(minLoc.X, minLoc.Y, maskwidth, maskheight);
                OpenCvSharp.CPlusPlus.Size masksize = new OpenCvSharp.CPlusPlus.Size(maskwidth, maskheight);
                Mat mask = new Mat(masksize, match.Type());

                mask.SetTo(new Scalar(1));

                match[maskROI] = mask;
            }

            #endregion

            //plot vertical lines
            #region plot vertical line
            OpenCvSharp.CPlusPlus.Point firstpt = new OpenCvSharp.CPlusPlus.Point(0, 0);
            OpenCvSharp.CPlusPlus.Point[] pos = new OpenCvSharp.CPlusPlus.Point[40];
            List<OpenCvSharp.CPlusPlus.Point> matchpos_buffer = new List<OpenCvSharp.CPlusPlus.Point>(matchpos);
            double[] vertiangle = new double[50];
            vertiangle.Initialize();
            int ptcount = 0;
            int linecount = 0;
            int verticount = 0;
            colnum = 0;
            rownum = 0;
            while (ptcount < totalmatch)
            {
                linecount = 0;
                for (int q = 0; q < totalmatch; q++)
                {
                    if (matchpos_buffer[q].X >= 0)
                    {
                        firstpt = matchpos_buffer[q];
                        colnum++;
                        colval.Add(firstpt.X);
                        break;
                    }
                }

                for (int q = 0; q < totalmatch; q++)
                {
                    if (matchpos_buffer[q].X >= 0)
                    {
                        if ((matchpos_buffer[q].X > (firstpt.X - Xdiff)) && (matchpos_buffer[q].X < (firstpt.X + Xdiff)))
                        {
                            pos[linecount] = matchpos_buffer[q];
                            linecount++;
                            ptcount++;
                            matchpos_buffer[q] = new OpenCvSharp.CPlusPlus.Point(-100, -100);
                        }
                    }
                }

                if (linecount > 3)
                {
                    //calculate best fit line
                    double meanX = 0;
                    double meanY = 0;
                    for (int q = 0; q < linecount; q++)
                    {
                        meanX += (double)pos[q].X;
                        meanY += (double)pos[q].Y;
                    }
                    meanX = meanX / (double)linecount;
                    meanY = meanY / (double)linecount;
                    double top = 0;
                    double btm = 0;
                    for (int q = 0; q < linecount; q++)
                    {
                        top += ((double)pos[q].X - meanX) * ((double)pos[q].Y - meanY);
                        btm += Math.Pow(((double)pos[q].X - meanX), 2);
                    }
                    double m = top / btm;
                    vertiangle[verticount] = (Math.Atan(m) / Math.PI) * 180;
                    verticount++;
                }
            }
            #endregion

            //plot horizontal lines
            #region plot horizontal lines
            firstpt = new OpenCvSharp.CPlusPlus.Point(0, 0);
            pos = new OpenCvSharp.CPlusPlus.Point[40];
            matchpos_buffer = new List<OpenCvSharp.CPlusPlus.Point>(matchpos);
            double[] horiangle = new double[50];
            horiangle.Initialize();
            ptcount = 0;
            linecount = 0;
            int horicount = 0;
            //group coordinates into lines
            while (ptcount < totalmatch)
            {
                linecount = 0;
                for (int q = 0; q < totalmatch; q++)
                {
                    if (matchpos_buffer[q].X >= 0)
                    {
                        firstpt = matchpos_buffer[q];
                        rownum++;
                        rowval.Add(firstpt.Y);
                        break;
                    }
                }

                for (int q = 0; q < totalmatch; q++)
                {
                    if (matchpos_buffer[q].X >= 0)
                    {
                        if ((matchpos_buffer[q].Y > (firstpt.Y - Ydiff)) && (matchpos_buffer[q].Y < (firstpt.Y + Ydiff)))
                        {
                            pos[linecount] = matchpos_buffer[q];
                            linecount++;
                            ptcount++;
                            matchpos_buffer[q] = new OpenCvSharp.CPlusPlus.Point(-100, -100);
                        }
                    }
                }

                if (linecount > 3)
                {
                    //calculate best fit line
                    double meanX = 0;
                    double meanY = 0;
                    for (int q = 0; q < linecount; q++)
                    {
                        meanX += (double)pos[q].X;
                        meanY += (double)pos[q].Y;
                        //WriteToLog("horipos: " + pos[q].X.ToString() + "," + pos[q].Y.ToString());
                    }
                    meanX = meanX / (double)linecount;
                    meanY = meanY / (double)linecount;
                    double top = 0;
                    double btm = 0;
                    for (int q = 0; q < linecount; q++)
                    {
                        top += ((double)pos[q].X - meanX) * ((double)pos[q].Y - meanY);
                        btm += Math.Pow(((double)pos[q].X - meanX), 2);
                    }

                    double m = top / btm;
                    horiangle[horicount] = (Math.Atan(m) / Math.PI) * 180;
                    horicount++;
                    //WriteToLog("hori grad: " + m.ToString());
                    //WriteToLog("hori angle: " + horiangle[horicount - 1].ToString() + "\n");
                }
            }
            #endregion

            //Calculate Final angle offset
            #region calculate final angle
            double vangle = 0;
            for (int q = 0; q < verticount; q++)
            {
                vangle += vertiangle[q];
            }
            vangle = vangle / verticount;
            WriteToLog("final verti angle: " + vangle.ToString());

            double hangle = 0;
            for (int q = 0; q < horicount; q++)
            {
                hangle += horiangle[q];
            }
            hangle = hangle / horicount;
            WriteToLog("final hori angle: " + hangle.ToString());

            if (vangle <= 0)
            {
                vangle += 90;
            }
            else
            {
                vangle -= 90;
            }
            if (double.IsNaN(vangle))
            {
                vangle = 0;
            }
            if (double.IsNaN(hangle))
            {
                hangle = 0;
            }
            double angle_offset = 0;
            angle_offset = (hangle + vangle) / 2;
            WriteToLog("Final angle offset: " + angle_offset.ToString() + "\n");
            #endregion

            Mat output = img.Clone();

            for (int i = 0; i < totalmatch; i++)
            {
                Point2f diecenterposition = new Point2f(matchpos[i].X, matchpos[i].Y);
                RotatedRect dierect = new RotatedRect(diecenterposition, new Size2f(12, 12), 0);
                output.Ellipse(dierect, new Scalar(0, 0, 255), 8);
            }
            WriteToLog("Col: " + colnum.ToString() + ", Row: " + rownum.ToString());
            WriteToLog("total match: " + matchpos.Count.ToString());
            displayImg(output);

            return angle_offset;
        }

        #endregion

        #endregion

        #region Event Handlers

        #region Main Image Controls

        private void btnOpenImg_Click(object sender, EventArgs e)
        {
            string imgname = "";

            OpenFileDialog dlg = new OpenFileDialog();

            dlg.Title = "Open Image";
            dlg.Filter = "Image Files (*.bmp, *.jpg, *.png)|*.bmp;*.jpg;*.png";

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                imgname = dlg.FileName;
                Bitmap bmp = new Bitmap(imgname);
                img = BitmapConverter.ToMat(bmp);
                if (img.Type() == MatType.CV_8UC1)
                {
                    Cv2.CvtColor(img, img, ColorConversion.GrayToBgr);
                }
                imgDisplay.Image = bmp;
                WriteToLog("Loaded Img: " + img.Type().ToString());
            }
            dlg.Dispose();
        }

        private void btnReset_Click(object sender, EventArgs e)
        {
            displayImg(img);
        }

        private void btnFill_Click(object sender, EventArgs e)
        {

            float ratioX = (float)imgDisplay.Width / (float)imgDisplay.Image.Width;
            float ratioY = (float)imgDisplay.Height / (float)imgDisplay.Image.Height;

            if (ratioX > ratioY)
            {
                imgDisplay.Zoom = ratioY;
            }
            else
            {
                imgDisplay.Zoom = ratioX;
            }
        }

        private void btnZoomIn_Click(object sender, EventArgs e)
        {
            imgDisplay.Zoom = imgDisplay.Zoom + 0.05f;
        }

        private void btnZoomOut_Click(object sender, EventArgs e)
        {
            imgDisplay.Zoom = imgDisplay.Zoom - 0.05f;
        }

        private void btnQueueMode_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                //deactivate queue mode
                queuemode = false;
                grpFilter.BackColor = Control.DefaultBackColor;
                grpQueue.Enabled = false;

            }
            else
            {
                //activate queue mode
                queuemode = true;
                grpFilter.BackColor = Color.Yellow;
                grpQueue.Enabled = true;
            }
        }

        #endregion

        #region Filters/Algos

        private void btnGrayscale_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("Grayscale");
                lstAlgo.Add(Algos.Grayscale);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();

                gray = toGrayscale(img);
                WriteToLog("Gray: " + gray.Type().ToString());

                gray.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnThreshold_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("Threshold");
                lstAlgo.Add(Algos.Threshold);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat BW = new Mat();

                gray = toGrayscale(img);
                BW = toBW(gray);
                WriteToLog("BW: " + BW.Type().ToString());

                gray.Dispose();
                BW.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnGaussian_Click(object sender, EventArgs e)
        {
            int sz = Convert.ToInt32(txtboxGaussianSize.Text.ToString());

            if ((sz % 2) == 0)
            {
                sz++;
                txtboxGaussianSize.Text = sz.ToString();
            }

            if (queuemode)
            {
                lstCmd.Items.Add("Gaussian");
                lstAlgo.Add(Algos.Gaussian);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat Gaussian = new Mat();

                Gaussian = toGaussian(img);
                WriteToLog("Gaussian: " + Gaussian.Type().ToString());

                Gaussian.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnCanny_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("Canny");
                lstAlgo.Add(Algos.Canny);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat BW = new Mat();
                Mat canny = new Mat();

                gray = toGrayscale(img);
                BW = toBW(gray);
                canny = toCanny(BW);
                WriteToLog("Canny: " + canny.Type().ToString());

                gray.Dispose();
                BW.Dispose();
                canny.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnHough_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("Hough1");
                lstAlgo.Add(Algos.Hough1);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat BW = new Mat();
                Mat canny = new Mat();
                Mat hough = new Mat();

                gray = toGrayscale(img);
                BW = toBW(gray);
                canny = toCanny(BW);
                hough = toHough(canny);
                WriteToLog("Hough1: " + hough.Type().ToString());

                gray.Dispose();
                BW.Dispose();
                canny.Dispose();
                hough.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnHough2_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("Hough2");
                lstAlgo.Add(Algos.Hough2);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat BW = new Mat();
                Mat canny = new Mat();
                Mat hough2 = new Mat();

                gray = toGrayscale(img);
                BW = toBW(gray);
                canny = toCanny(BW);
                hough2 = toHough2(canny);
                WriteToLog("Hough2: " + hough2.Type().ToString());

                gray.Dispose();
                BW.Dispose();
                canny.Dispose();
                hough2.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnHoughCircle_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("HoughCircle");
                lstAlgo.Add(Algos.HoughCircle);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat BW = new Mat();
                Mat canny = new Mat();
                Mat houghcircle = new Mat();

                gray = toGrayscale(img);
                BW = toBW(gray);
                canny = toCanny(BW);
                houghcircle = toHoughCircle(canny);
                WriteToLog("HoughCircle: " + houghcircle.Type().ToString());

                gray.Dispose();
                BW.Dispose();
                canny.Dispose();
                houghcircle.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnFourier_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("Fourier");
                lstAlgo.Add(Algos.Fourier);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat fourier = new Mat();

                gray = toGrayscale(img);
                fourier = toFourier(gray);
                WriteToLog("Fourier: " + fourier.Type().ToString());

                fourier.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnInvert_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("Invert");
                lstAlgo.Add(Algos.Invert);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat invert = new Mat();

                gray = toGrayscale(img);
                invert = toInvert(gray);
                WriteToLog("Invert: " + invert.Type().ToString());

                gray.Dispose();
                invert.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnFindBlob_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("FindBlob");
                lstAlgo.Add(Algos.FindBlob);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat BW = new Mat();
                Mat blob = new Mat();

                gray = toGrayscale(img);
                BW = toBW(gray);
                blob = toFindBlob(BW);
                WriteToLog("Blob: " + blob.Type().ToString());

                gray.Dispose();
                BW.Dispose();
                blob.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnEqualize_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("Equalize");
                lstAlgo.Add(Algos.Equalize);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat equalize = new Mat();

                gray = toGrayscale(img);
                equalize = toEqualize(gray);
                WriteToLog("Equalize: " + equalize.Type().ToString());

                gray.Dispose();
                equalize.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnAdaptiveThreshold_Click(object sender, EventArgs e)
        {
            int sz = Convert.ToInt32(txtboxAdaptiveSize.Text.ToString());

            if ((sz % 2) == 0)
            {
                sz++;
                txtboxAdaptiveSize.Text = sz.ToString();
            }
            else if (sz <= 0)
            {
                sz = 9;
                txtboxAdaptiveSize.Text = sz.ToString();
            }

            if (queuemode)
            {
                lstCmd.Items.Add("AdaptiveThres");
                lstAlgo.Add(Algos.AdaptiveThres);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat adaptiveBW = new Mat();

                gray = toGrayscale(img);
                adaptiveBW = toAdaptiveThres(gray);
                WriteToLog("Adaptive Treshold: " + adaptiveBW.Type().ToString());

                gray.Dispose();
                adaptiveBW.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnErosion_Click(object sender, EventArgs e)
        {
            int sz = Convert.ToInt32(txtboxErosionSize.Text.ToString());
            if (sz <= 0)
            {
                sz = 1;
                txtboxErosionSize.Text = sz.ToString();
            }

            if (queuemode)
            {
                lstCmd.Items.Add("Erosion");
                lstAlgo.Add(Algos.Erosion);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat BW = new Mat();
                Mat Erode = new Mat();

                gray = toGrayscale(img);
                BW = toBW(gray);
                Erode = toErosion(BW);
                WriteToLog("Erode: " + Erode.Type().ToString());

                gray.Dispose();
                BW.Dispose();
                Erode.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnDilation_Click(object sender, EventArgs e)
        {
            int sz = Convert.ToInt32(txtboxDilationSize.Text.ToString());
            if (sz <= 0)
            {
                sz = 1;
                txtboxDilationSize.Text = sz.ToString();
            }

            if (queuemode)
            {
                lstCmd.Items.Add("Dilation");
                lstAlgo.Add(Algos.Dilation);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat BW = new Mat();
                Mat Dilate = new Mat();

                gray = toGrayscale(img);
                BW = toBW(gray);
                Dilate = toDilation(BW);
                WriteToLog("Dilate: " + Dilate.Type().ToString());

                gray.Dispose();
                BW.Dispose();
                Dilate.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnContour_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("Contour");
                lstAlgo.Add(Algos.Contour);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat BW = new Mat();
                Mat contour = new Mat();

                gray = toGrayscale(img);
                BW = toBW(gray);
                contour = toContour(BW);
                WriteToLog("contour: " + contour.Type().ToString());

                gray.Dispose();
                BW.Dispose();
                contour.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnFloodFill_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("FloodFill");
                lstAlgo.Add(Algos.FloodFill);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat BW = new Mat();
                Mat blob = new Mat();
                Mat floodfill = new Mat();

                gray = toGrayscale(img);
                BW = toBW(gray);
                blob = toFindBlob(BW);
                floodfill = toFloodFill(BW);
                WriteToLog("floodfill: " + floodfill.Type().ToString());

                gray.Dispose();
                BW.Dispose();
                blob.Dispose();
                floodfill.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnGabor_Click(object sender, EventArgs e)
        {
            int sz = Convert.ToInt32(txtboxGaborSize.Text.ToString());

            if ((sz % 2) == 0)
            {
                sz++;
                txtboxGaborSize.Text = sz.ToString();
            }

            if (queuemode)
            {
                lstCmd.Items.Add("Gabor");
                lstAlgo.Add(Algos.Gabor);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat gabor = new Mat();

                gray = toGrayscale(img);
                gabor = toGabor(gray);
                WriteToLog("gabor: " + gabor.Type().ToString());

                gray.Dispose();
                gabor.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        //Needs improvement
        //hidden now
        private void btnWatershed_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("Watershed");
                lstAlgo.Add(Algos.Watershed);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat watershed = new Mat();

                watershed = toWatershed(img);
                WriteToLog("watershed: " + watershed.Type().ToString());

                watershed.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        //hidden now
        private void btnDistance_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("Distance");
                lstAlgo.Add(Algos.Distance);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat BW = new Mat();
                Mat distance = new Mat();

                gray = toGrayscale(img);
                BW = toBW(gray);
                distance = toDistance(BW);
                WriteToLog("distance: " + distance.Type().ToString());

                gray.Dispose();
                BW.Dispose();
                distance.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnAveGabor_Click(object sender, EventArgs e)
        {
            int sz = Convert.ToInt32(txtboxGaborSize.Text.ToString());

            if ((sz % 2) == 0)
            {
                sz++;
                txtboxGaborSize.Text = sz.ToString();
            }

            if (queuemode)
            {
                lstCmd.Items.Add("AveGabor");
                lstAlgo.Add(Algos.AveGabor);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat avegabor = new Mat();

                gray = toGrayscale(img);
                avegabor = toAveGabor(gray);
                WriteToLog("avegabor: " + avegabor.Type().ToString());

                gray.Dispose();
                avegabor.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnKmeans_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("Kmeans");
                lstAlgo.Add(Algos.Kmeans);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat kmeans = new Mat();

                gray = toGrayscale(img);
                kmeans = toKmeans(gray);
                WriteToLog("kmeans: " + kmeans.Type().ToString());

                gray.Dispose();
                kmeans.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void cboKmeansFilter_SelectedIndexChanged(object sender, EventArgs e)
        {
            int sz = Convert.ToInt32(txtboxKmeansSize.Text);
            Mat temp = new Mat();

            if (cboKmeansFilter.SelectedIndex > 0)
            {   //not display all kmeans layer
                int lowerrange = ((cboKmeansFilter.SelectedIndex - 1) * (255 / (sz - 1))) - 1;
                int upperrange = ((cboKmeansFilter.SelectedIndex - 1) * (255 / (sz - 1))) + 1;
                Mat lowerb = new Mat(kmeansglobal.Size(), kmeansglobal.Type(), new Scalar(lowerrange));
                Mat upperb = new Mat(kmeansglobal.Size(), kmeansglobal.Type(), new Scalar(upperrange));

                temp = kmeansglobal.InRange(lowerb, upperb);
            }
            else
            {
                temp = kmeansglobal.Clone();
            }

            displayImg(temp);
        }

        private void btnThinning_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("Thinning");
                lstAlgo.Add(Algos.Thinning);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat BW = new Mat();
                Mat thinning = new Mat();

                gray = toGrayscale(img);
                BW = toBW(gray);
                thinning = toThinning (BW);
                WriteToLog("thinning: " + thinning.Type().ToString());

                gray.Dispose();
                BW.Dispose();
                thinning.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnFitGrid_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("FitGrid");
                lstAlgo.Add(Algos.FitGrid);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat BW = new Mat();
                Mat fitgrid = new Mat();

                gray = toGrayscale(img);
                BW = toBW(gray);
                fitgrid = toFitGrid(BW);
                WriteToLog("fitgrid: " + fitgrid.Type().ToString());

                gray.Dispose();
                BW.Dispose();
                fitgrid.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btn2Gabor_Click(object sender, EventArgs e)
        {
            int sz = Convert.ToInt32(txtboxGaborSize.Text.ToString());

            if ((sz % 2) == 0)
            {
                sz++;
                txtboxGaborSize.Text = sz.ToString();
            }

            if (queuemode)
            {
                lstCmd.Items.Add("TwoGabor");
                lstAlgo.Add(Algos.TwoGabor);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat twogabor = new Mat();

                gray = toGrayscale(img);
                twogabor = toTwoGabor(gray);
                WriteToLog("twogabor: " + twogabor.Type().ToString());

                gray.Dispose();
                twogabor.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnSharpen_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("Sharpen");
                lstAlgo.Add(Algos.Sharpen);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat Sharpen = new Mat();

                Sharpen = toSharpen(img);
                WriteToLog("Gaussian: " + Sharpen.Type().ToString());

                Sharpen.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        //TO BE COMPLETED - Finetuning
        private void btnDieHough_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("DieHough");
                lstAlgo.Add(Algos.DieHough);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat gray = new Mat();
                Mat adaptiveBW = new Mat();
                Mat canny = new Mat();
                Mat diehough = new Mat();

                gray = toGrayscale(img);
                adaptiveBW = toAdaptiveThres(gray);
                canny = toCanny(adaptiveBW);
                diehough = toDieHough(canny);
                WriteToLog("diehough: " + diehough.Type().ToString());

                gray.Dispose();
                adaptiveBW.Dispose();
                canny.Dispose();
                diehough.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        private void btnMedian_Click(object sender, EventArgs e)
        {
            if (queuemode)
            {
                lstCmd.Items.Add("Median");
                lstAlgo.Add(Algos.Median);
            }
            else
            {
                timer.Reset();
                timer.Start();

                Mat Median = new Mat();

                Median = toGaussian(img);
                WriteToLog("Median: " + Median.Type().ToString());

                Median.Dispose();

                timer.Stop();
                UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
            }
        }

        //Advance algos--------------------------------------------------------------------------------------------------

        //actually angle align
        private void btnAutoStart_Click(object sender, EventArgs e)
        {
            timer.Reset();
            timer.Start();

            autostart = true;

            Mat gray = new Mat();
            Mat twogabor = new Mat();
            Mat gaussian = new Mat();
            Mat adaptivethreshold = new Mat();
            Mat fitgrid = new Mat();
    
            //grayscale
            gray = toGrayscale(img);

            //gray = toInvert(gray);

            //2gabor
            //size: 31      lambda: 10      sigma: 5
            //gamma: 0.1    theta: 0        Psi: 0
            twogabor = toTwoGabor(gray);

            //gaussian
            //size: 35      sigma: 0
            gaussian = toGaussian(twogabor);

            //adaptive threshold
            //size: 45      type: Mean
            adaptivethreshold = toAdaptiveThres(gaussian);

            //test
            //adaptivethreshold = toInvert(adaptivethreshold);

            //fit grid
            //detect textbox
            fitgrid = toFitGrid(adaptivethreshold);

            toMatchDieTemplate();

            autostart = false;

            gray.Dispose();
            twogabor.Dispose();
            gaussian.Dispose();
            adaptivethreshold.Dispose();
            fitgrid.Dispose();

            timer.Stop();
            UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
        }

        private void btnStats_Click(object sender, EventArgs e)
        {
            timer.Reset();
            timer.Start();

            autostart = true;

            Mat gray = new Mat();
            Mat twogabor = new Mat();
            Mat gaussian = new Mat();
            Mat adaptivethreshold = new Mat();
            Mat fitgrid = new Mat();
            Mat statsmean = new Mat();
            Mat statsSD = new Mat();

            //grayscale
            gray = toGrayscale(img);

            //2gabor
            //size: 31      lambda: 10      sigma: 5
            //gamma: 0.1    theta: 0        Psi: 0
            twogabor = toTwoGabor(gray);

            //gaussian
            //size: 35      sigma: 0
            gaussian = toGaussian(twogabor);

            //adaptive threshold
            //size: 45      type: Mean
            adaptivethreshold = toAdaptiveThres(gaussian);

            //fit grid
            //detect textbox
            fitgrid = toFitGrid(adaptivethreshold);

            //diematch
            toMatchDieTemplate();

            statsmean = toStatsMean(img);

            statsSD = toStatsStdDev(img, statsmean);

            toStatsZscore(img, statsmean, statsSD);

            autostart = false;

            timer.Stop();
            UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
        }

        private void btnMap_Click(object sender, EventArgs e)
        {
            timer.Reset();
            timer.Start();

            autostart = true;

            Mat gray = new Mat();
            Mat twogabor = new Mat();
            Mat gaussian = new Mat();
            Mat adaptivethreshold = new Mat();
            Mat fitgrid = new Mat();

            //grayscale
            gray = toGrayscale(img);

            //2gabor
            //size: 31      lambda: 10      sigma: 5
            //gamma: 0.1    theta: 0        Psi: 0
            twogabor = toTwoGabor(gray);

            //gaussian
            //size: 35      sigma: 0
            gaussian = toGaussian(twogabor);

            //adaptive threshold
            //size: 45      type: Mean
            adaptivethreshold = toAdaptiveThres(gaussian);

            //fit grid
            //detect textbox
            fitgrid = toFitGrid(adaptivethreshold);

            toMatchDieTemplate();

            toCreate2DMap();

            autostart = false;

            gray.Dispose();
            twogabor.Dispose();
            gaussian.Dispose();
            adaptivethreshold.Dispose();
            fitgrid.Dispose();

            timer.Stop();
            UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
        }

        private void btnFullAuto_Click(object sender, EventArgs e)
        {
            timer.Reset();
            timer.Start();

            //WIP
            autostart = true;
            fullauto = true;

            Point2f dist = new Point2f();
            Point2f diesize = new Point2f();
            Mat gray = new Mat();
            Mat fourier = new Mat();
            Mat twogabor = new Mat();
            Mat gaussian = new Mat();
            Mat adaptivethreshold = new Mat();
            Mat fitgrid = new Mat();
            Mat sharpen = new Mat();

            gray = toGrayscale(img);
            gaussian = toGaussian(gray);
            fourier = toFourier(gaussian);
            sharpen = toSharpen(fourier);
            dist = findFourierDist(sharpen);
            diesize = calculateDieSize(dist);
            fouriergridwidth = (int)diesize.X;
            fouriergridheight = (int)diesize.Y;

            //2gabor
            //size: 31      lambda: 10      sigma: 5
            //gamma: 0.1    theta: 0        Psi: 0
//            twogabor = toTwoGabor(gray);

            //gaussian
            //size: 35      sigma: 0
//            gaussian = toGaussian(twogabor);

            //adaptive threshold
            //size: 45      type: Mean
//            adaptivethreshold = toAdaptiveThres(gaussian);

            //fit grid
            //detect textbox
//            fitgrid = toFitGrid(adaptivethreshold);
            rdDieMatch.Checked = true;
            diematchmode = false;
//            toMatchDieTemplate();

            gray.Dispose();
            fourier.Dispose();
            twogabor.Dispose();
            gaussian.Dispose();
            adaptivethreshold.Dispose();
            fitgrid.Dispose();

            autostart = false;
            fullauto = false;

            timer.Stop();
            UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
        }

        private void btnFindCorner1_Click(object sender, EventArgs e)
        {
            string imgname = "";

            OpenFileDialog dlg = new OpenFileDialog();

            dlg.Title = "Open Image";
            dlg.Filter = "Image Files (*.bmp, *.jpg, *.png)|*.bmp;*.jpg;*.png";

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                imgname = dlg.FileName;
                Bitmap bmp = new Bitmap(imgname);
                diecorner1 = BitmapConverter.ToMat(bmp);
                if (diecorner1.Type() == MatType.CV_8UC1)
                {
                    Cv2.CvtColor(diecorner1, diecorner1, ColorConversion.GrayToBgr);
                }
                imgDisplay.Image = bmp;
            }
            dlg.Dispose();

            timer.Reset();
            timer.Start();

            //autostart = true;
            diecornerstage = 1;

            Mat gray = new Mat();
            Mat twogabor = new Mat();
            Mat gaussian = new Mat();
            Mat adaptivethreshold = new Mat();
            Mat fitgrid = new Mat();

            //grayscale
            gray = toGrayscale(diecorner1);

            //2gabor
            //size: 31      lambda: 10      sigma: 5
            //gamma: 0.1    theta: 0        Psi: 0
            twogabor = toTwoGabor(gray);

            //gaussian
            //size: 35      sigma: 0
            gaussian = toGaussian(twogabor);

            //adaptive threshold
            //size: 45      type: Mean
            adaptivethreshold = toAdaptiveThres(gaussian);

            //fit grid
            //detect textbox
            fitgrid = toFitGrid(adaptivethreshold);

            toMatchDieTemplate();

            diecornermean = toStatsMean(diecorner1);

            diecornerSD = toStatsStdDev(diecorner1, diecornermean);

            diecornerstage = 0;

            gray.Dispose();
            twogabor.Dispose();
            gaussian.Dispose();
            adaptivethreshold.Dispose();
            fitgrid.Dispose();

            timer.Stop();
            UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
        }

        private void btnFindCorner2_Click(object sender, EventArgs e)
        {
            string imgname = "";

            OpenFileDialog dlg = new OpenFileDialog();

            dlg.Title = "Open Image";
            dlg.Filter = "Image Files (*.bmp, *.jpg, *.png)|*.bmp;*.jpg;*.png";

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                imgname = dlg.FileName;
                Bitmap bmp = new Bitmap(imgname);
                diecorner2 = BitmapConverter.ToMat(bmp);
                if (diecorner2.Type() == MatType.CV_8UC1)
                {
                    Cv2.CvtColor(diecorner2, diecorner2, ColorConversion.GrayToBgr);
                }
                imgDisplay.Image = bmp;
            }
            dlg.Dispose();

            diecornerstage = 2;

            toMatchDieTemplate();

            toStatsZscore(diecorner2, diecornermean, diecornerSD);

            toCreate2DMap();

            diecornerstage = 0;
            /*
            int colnum = 7;
            int rownum = 4;
            int[,] array = new int[,] {{1,1,1,1,1,1,1},
                                       {1,1,1,1,1,1,0},
                                       {1,1,1,1,0,0,0},
                                       {1,1,0,0,0,0,0}};

            int width = Convert.ToInt32(txtboxFitGridWidth.Text);
            int height = Convert.ToInt32(txtboxFitGridHeight.Text);
            double ratio = Convert.ToDouble(txtboxFitGridRatio.Text);
            int gridwidth = (int)(width / ratio);
            int gridheight = (int)(height / ratio);

            int matwidth = gridwidth * colnum;
            int matheight = gridheight * rownum;

            Mat matching = new Mat(matheight, matwidth, MatType.CV_8UC1, new Scalar(255));
            Mat submat = new Mat();
            Rect dierect = new Rect();

            for (int y = 0; y < rownum; y++)
            {
                for (int x = 0; x < colnum; x++)
                {
                    if (array[y, x] == 1)
                    {
                        dierect = new Rect(new OpenCvSharp.CPlusPlus.Point(x * gridwidth, y * gridheight), new OpenCvSharp.CPlusPlus.Size(gridwidth, gridheight));
                        submat = matching.SubMat(dierect);
                        diecornermean.CopyTo(submat);
                    }
                }
            }
            Cv2.ImShow("matching", matching);
            Mat match = new Mat();
            Cv2.MatchTemplate(diecorner2, matching, match, MatchTemplateMethod.SqDiffNormed);
            Cv2.ImShow("match", match);
            double minVal, maxVal;
            OpenCvSharp.CPlusPlus.Point minLoc, maxLoc;
            Cv2.MinMaxLoc(match, out minVal, out maxVal, out minLoc, out maxLoc);
            diecorner2.Ellipse(new RotatedRect(new Point2f(minLoc.X, minLoc.Y), new Size2f(20, 20), 0), new Scalar(255), 20);
            displayImg(diecorner2);
            //matchLoc = minLoc;
            */
        }

        private void btnAngleAlign2_Click(object sender, EventArgs e)
        {
            int width = Convert.ToInt32(txtboxFitGridWidth.Text);
            int height = Convert.ToInt32(txtboxFitGridHeight.Text);
            double ratio = Convert.ToDouble(txtboxFitGridRatio.Text);
            int modelwidth = (int)(width / ratio);
            int modelheight = (int)(height / ratio);
            double angleoffset = 0;

            Mat model = ExtractMatchingModel(img, modelwidth, modelheight);
            angleoffset = MatchDie2(img, model, modelwidth, modelheight);

            WriteToLog("Angle Offset: " + angleoffset.ToString());
        }

        //Advance algos - End -------------------------------------------------------------------------------------------------

        private void rdDieMatch_CheckedChanged(object sender, EventArgs e)
        {
            if (rdDieMatch.Checked)
            {
                diematchmode = false;
            }
        }

        private void rdMinScore_CheckedChanged(object sender, EventArgs e)
        {
            if (rdMinScore.Checked)
            {
                diematchmode = true;
            }
        }

        #endregion

        #region Queuing

        private void btnExecuteCmd_Click(object sender, EventArgs e)
        {
            timer.Reset();
            timer.Start();

            Mat output = new Mat();
            Mat temp = new Mat();
            output = img.Clone();

            for (int i = 0; i < lstAlgo.Count; i++)
            {
                switch (lstAlgo[i])
                {
                    case Algos.Grayscale:
                        output = toGrayscale(output);
                        break;
                    case Algos.Threshold:
                        output = toBW(output);
                        break;
                    case Algos.Gaussian:
                        output = toGaussian(output);
                        break;
                    case Algos.Hough1:
                        output = toHough(output);
                        break;
                    case Algos.Hough2:
                        output = toHough2(output);
                        break;
                    case Algos.HoughCircle:
                        output = toHoughCircle(output);
                        break;
                    case Algos.Canny:
                        output = toCanny(output);
                        break;
                    case Algos.Fourier:
                        output = toFourier(output);
                        break;
                    case Algos.Invert:
                        output = toInvert(output);
                        break;
                    case Algos.FindBlob:
                        output = toFindBlob(output);
                        break;
                    case Algos.Equalize:
                        output = toEqualize(output);
                        break;
                    case Algos.AdaptiveThres:
                        output = toAdaptiveThres(output);
                        break;
                    case Algos.Erosion:
                        output = toErosion(output);
                        break;
                    case Algos.Dilation:
                        output = toDilation(output);
                        break;
                    case Algos.Contour:
                        output = toContour(output);
                        break;
                    case Algos.FloodFill:
                        temp = toFindBlob(output);
                        output = toFloodFill(output);
                        break;
                    case Algos.Gabor:
                        output = toGabor(output);
                        break;
                    case Algos.Watershed:
                        output = toWatershed(output);
                        break;
                    case Algos.Distance:
                        output = toDistance(output);
                        break;
                    case Algos.AveGabor:
                        output = toAveGabor(output);
                        break;
                    case Algos.Kmeans:
                        output = toKmeans(output);
                        break;
                    case Algos.Thinning:
                        output = toThinning(output);
                        break;
                    case Algos.FitGrid:
                        output = toFitGrid(output);
                        break;
                    case Algos.TwoGabor:
                        output = toTwoGabor(output);
                        break;
                    case Algos.Sharpen:
                        output = toSharpen(output);
                        break;
                    default:
                        break;
                }
            }

            timer.Stop();
            UpdateTimer(string.Format("{0}:{1}:{2}", timer.Elapsed.Minutes, timer.Elapsed.Seconds, timer.Elapsed.TotalMilliseconds));
        }

        private void btnRemoveCmd_Click(object sender, EventArgs e)
        {
            int index = lstCmd.SelectedIndex;
            if (index >= 0)
            {
                lstCmd.Items.RemoveAt(index);
                lstAlgo.RemoveAt(index);
            }
        }

        private void btnShowQ_Click(object sender, EventArgs e)
        {
            WriteToLog("Queue Start:");
            for (int i = 0; i < lstAlgo.Count; i++)
            {
                WriteToLog(lstAlgo[i].ToString());
            }
            WriteToLog("Queue End");
        }

        private void btnQup_Click(object sender, EventArgs e)
        {
            int index = lstCmd.SelectedIndex;
            if (index >= 0)
            {
                Algos item = new Algos();
                String itemstr = "";
                itemstr = lstCmd.SelectedItem.ToString();
                WriteToLog("Selected: " + itemstr);
                switch (itemstr)
                {
                    case "Grayscale":
                        item = Algos.Grayscale;
                        break;
                    case "Threshold":
                        item = Algos.Threshold;
                        break;
                    case "Gaussian":
                        item = Algos.Gaussian;
                        break;
                    case "Hough1":
                        item = Algos.Hough1;
                        break;
                    case "Hough2":
                        item = Algos.Hough2;
                        break;
                    case "HoughCircle":
                        item = Algos.HoughCircle;
                        break;
                    case "Canny":
                        item = Algos.Canny;
                        break;
                    case "Fourier":
                        item = Algos.Fourier;
                        break;
                    case "Invert":
                        item = Algos.Invert;
                        break;
                    case "FindBlob":
                        item = Algos.FindBlob;
                        break;
                    case "Equalize":
                        item = Algos.Equalize;
                        break;
                    case "AdaptiveThres":
                        item = Algos.AdaptiveThres;
                        break;
                    case "Erosion":
                        item = Algos.Erosion;
                        break;
                    case "Dilation":
                        item = Algos.Dilation;
                        break;
                    case "Contour":
                        item = Algos.Contour;
                        break;
                    case "FloodFill":
                        item = Algos.FloodFill;
                        break;
                    case "Gabor":
                        item = Algos.Gabor;
                        break;
                    case "Watershed":
                        item = Algos.Watershed;
                        break;
                    case "Distance":
                        item = Algos.Distance;
                        break;
                    case "AveGabor":
                        item = Algos.AveGabor;
                        break;
                    case "Kmeans":
                        item = Algos.Kmeans;
                        break;
                    case "Thinning":
                        item = Algos.Thinning;
                        break;
                    case "FitGrid":
                        item = Algos.FitGrid;
                        break;
                    case "TwoGabor":
                        item = Algos.TwoGabor;
                        break;
                    case "Sharpen":
                        item = Algos.Sharpen;
                        break;
                    default:

                        break;
                }
                lstAlgo.Insert(index - 1, item);
                lstCmd.Items.Insert(index - 1, itemstr);
                lstAlgo.RemoveAt(index + 1);
                lstCmd.Items.RemoveAt(index + 1);
            }
        }

        private void btnQdown_Click(object sender, EventArgs e)
        {
            int index = lstCmd.SelectedIndex;
            if (index >= 0)
            {
                Algos item = new Algos();
                String itemstr = "";
                itemstr = lstCmd.SelectedItem.ToString();
                WriteToLog("Selected: " + itemstr);
                switch (itemstr)
                {
                    case "Grayscale":
                        item = Algos.Grayscale;
                        break;
                    case "Threshold":
                        item = Algos.Threshold;
                        break;
                    case "Gaussian":
                        item = Algos.Gaussian;
                        break;
                    case "Hough1":
                        item = Algos.Hough1;
                        break;
                    case "Hough2":
                        item = Algos.Hough2;
                        break;
                    case "HoughCircle":
                        item = Algos.HoughCircle;
                        break;
                    case "Canny":
                        item = Algos.Canny;
                        break;
                    case "Fourier":
                        item = Algos.Fourier;
                        break;
                    case "Invert":
                        item = Algos.Invert;
                        break;
                    case "FindBlob":
                        item = Algos.FindBlob;
                        break;
                    case "Equalize":
                        item = Algos.Equalize;
                        break;
                    case "AdaptiveThres":
                        item = Algos.AdaptiveThres;
                        break;
                    case "Erosion":
                        item = Algos.Erosion;
                        break;
                    case "Dilation":
                        item = Algos.Dilation;
                        break;
                    case "Contour":
                        item = Algos.Contour;
                        break;
                    case "FloodFill":
                        item = Algos.FloodFill;
                        break;
                    case "Gabor":
                        item = Algos.Gabor;
                        break;
                    case "Watershed":
                        item = Algos.Watershed;
                        break;
                    case "Distance":
                        item = Algos.Distance;
                        break;
                    case "AveGabor":
                        item = Algos.AveGabor;
                        break;
                    case "Kmeans":
                        item = Algos.Kmeans;
                        break;
                    case "Thinning":
                        item = Algos.Thinning;
                        break;
                    case "FitGrid":
                        item = Algos.FitGrid;
                        break;
                    case "TwoGabor":
                        item = Algos.TwoGabor;
                        break;
                    case "Sharpen":
                        item = Algos.Sharpen;
                        break;
                    default:

                        break;
                }
                lstAlgo.Insert(index + 2, item);
                lstCmd.Items.Insert(index + 2, itemstr);
                lstAlgo.RemoveAt(index);
                lstCmd.Items.RemoveAt(index);
            }
        }

        #endregion

        #endregion

        #region App Closing

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            img.Dispose();
        }

        #endregion 

        
   
    }
}
